roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default true
2460  * @cfg {String} size (sm|lg) default empty
2461  * 
2462  * 
2463  * @constructor
2464  * Create a new Modal Dialog
2465  * @param {Object} config The config object
2466  */
2467
2468 Roo.bootstrap.Modal = function(config){
2469     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470     this.addEvents({
2471         // raw events
2472         /**
2473          * @event btnclick
2474          * The raw btnclick event for the button
2475          * @param {Roo.EventObject} e
2476          */
2477         "btnclick" : true
2478     });
2479     this.buttons = this.buttons || [];
2480      
2481     if (this.tmpl) {
2482         this.tmpl = Roo.factory(this.tmpl);
2483     }
2484     
2485 };
2486
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2488     
2489     title : 'test dialog',
2490    
2491     buttons : false,
2492     
2493     // set on load...
2494      
2495     html: false,
2496     
2497     tmp: false,
2498     
2499     specificTitle: false,
2500     
2501     buttonPosition: 'right',
2502     
2503     allow_close : true,
2504     
2505     animate : true,
2506     
2507     fitwindow: false,
2508     
2509     
2510      // private
2511     dialogEl: false,
2512     bodyEl:  false,
2513     footerEl:  false,
2514     titleEl:  false,
2515     closeEl:  false,
2516     
2517     size: '',
2518     
2519     
2520     onRender : function(ct, position)
2521     {
2522         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2523      
2524         if(!this.el){
2525             var cfg = Roo.apply({},  this.getAutoCreate());
2526             cfg.id = Roo.id();
2527             //if(!cfg.name){
2528             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2529             //}
2530             //if (!cfg.name.length) {
2531             //    delete cfg.name;
2532            // }
2533             if (this.cls) {
2534                 cfg.cls += ' ' + this.cls;
2535             }
2536             if (this.style) {
2537                 cfg.style = this.style;
2538             }
2539             this.el = Roo.get(document.body).createChild(cfg, position);
2540         }
2541         //var type = this.el.dom.type;
2542         
2543         
2544         if(this.tabIndex !== undefined){
2545             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2546         }
2547         
2548         this.dialogEl = this.el.select('.modal-dialog',true).first();
2549         this.bodyEl = this.el.select('.modal-body',true).first();
2550         this.closeEl = this.el.select('.modal-header .close', true).first();
2551         this.footerEl = this.el.select('.modal-footer',true).first();
2552         this.titleEl = this.el.select('.modal-title',true).first();
2553         
2554         
2555          
2556         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557         this.maskEl.enableDisplayMode("block");
2558         this.maskEl.hide();
2559         //this.el.addClass("x-dlg-modal");
2560     
2561         if (this.buttons.length) {
2562             Roo.each(this.buttons, function(bb) {
2563                 var b = Roo.apply({}, bb);
2564                 b.xns = b.xns || Roo.bootstrap;
2565                 b.xtype = b.xtype || 'Button';
2566                 if (typeof(b.listeners) == 'undefined') {
2567                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2568                 }
2569                 
2570                 var btn = Roo.factory(b);
2571                 
2572                 btn.render(this.el.select('.modal-footer div').first());
2573                 
2574             },this);
2575         }
2576         // render the children.
2577         var nitems = [];
2578         
2579         if(typeof(this.items) != 'undefined'){
2580             var items = this.items;
2581             delete this.items;
2582
2583             for(var i =0;i < items.length;i++) {
2584                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2585             }
2586         }
2587         
2588         this.items = nitems;
2589         
2590         // where are these used - they used to be body/close/footer
2591         
2592        
2593         this.initEvents();
2594         //this.el.addClass([this.fieldClass, this.cls]);
2595         
2596     },
2597     
2598     getAutoCreate : function(){
2599         
2600         
2601         var bdy = {
2602                 cls : 'modal-body',
2603                 html : this.html || ''
2604         };
2605         
2606         var title = {
2607             tag: 'h4',
2608             cls : 'modal-title',
2609             html : this.title
2610         };
2611         
2612         if(this.specificTitle){
2613             title = this.title;
2614             
2615         };
2616         
2617         var header = [];
2618         if (this.allow_close) {
2619             header.push({
2620                 tag: 'button',
2621                 cls : 'close',
2622                 html : '&times'
2623             });
2624         }
2625         
2626         header.push(title);
2627         
2628         var size = '';
2629         
2630         if(this.size.length){
2631             size = 'modal-' + this.size;
2632         }
2633         
2634         var modal = {
2635             cls: "modal",
2636             style : 'display: none',
2637             cn : [
2638                 {
2639                     cls: "modal-dialog " + size,
2640                     cn : [
2641                         {
2642                             cls : "modal-content",
2643                             cn : [
2644                                 {
2645                                     cls : 'modal-header',
2646                                     cn : header
2647                                 },
2648                                 bdy,
2649                                 {
2650                                     cls : 'modal-footer',
2651                                     cn : [
2652                                         {
2653                                             tag: 'div',
2654                                             cls: 'btn-' + this.buttonPosition
2655                                         }
2656                                     ]
2657                                     
2658                                 }
2659                                 
2660                                 
2661                             ]
2662                             
2663                         }
2664                     ]
2665                         
2666                 }
2667             ]
2668         };
2669         
2670         if(this.animate){
2671             modal.cls += ' fade';
2672         }
2673         
2674         return modal;
2675           
2676     },
2677     getChildContainer : function() {
2678          
2679          return this.bodyEl;
2680         
2681     },
2682     getButtonContainer : function() {
2683          return this.el.select('.modal-footer div',true).first();
2684         
2685     },
2686     initEvents : function()
2687     {
2688         if (this.allow_close) {
2689             this.closeEl.on('click', this.hide, this);
2690         }
2691         Roo.EventManager.onWindowResize(this.resize, this, true);
2692         
2693  
2694     },
2695     
2696     resize : function()
2697     {
2698         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2699         if (this.fitwindow) {
2700             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2702             this.setSize(w,h)
2703         }
2704     },
2705     
2706     setSize : function(w,h)
2707     {
2708         if (!w && !h) {
2709             return;
2710         }
2711         this.resizeTo(w,h);
2712     },
2713     
2714     show : function() {
2715         
2716         if (!this.rendered) {
2717             this.render();
2718         }
2719         
2720         this.el.setStyle('display', 'block');
2721         
2722         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2723             var _this = this;
2724             (function(){
2725                 this.el.addClass('in');
2726             }).defer(50, this);
2727         }else{
2728             this.el.addClass('in');
2729             
2730         }
2731         
2732         // not sure how we can show data in here.. 
2733         //if (this.tmpl) {
2734         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2735         //}
2736         
2737         Roo.get(document.body).addClass("x-body-masked");
2738         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2739         this.maskEl.show();
2740         this.el.setStyle('zIndex', '10001');
2741        
2742         this.fireEvent('show', this);
2743         this.items.forEach(function(e) {
2744             e.layout ? e.layout() : false;
2745                 
2746         });
2747         this.resize();
2748         
2749         
2750         
2751     },
2752     hide : function()
2753     {
2754         this.maskEl.hide();
2755         Roo.get(document.body).removeClass("x-body-masked");
2756         this.el.removeClass('in');
2757         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2758         
2759         if(this.animate){ // why
2760             var _this = this;
2761             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2762         }else{
2763             this.el.setStyle('display', 'none');
2764         }
2765         
2766         this.fireEvent('hide', this);
2767     },
2768     
2769     addButton : function(str, cb)
2770     {
2771          
2772         
2773         var b = Roo.apply({}, { html : str } );
2774         b.xns = b.xns || Roo.bootstrap;
2775         b.xtype = b.xtype || 'Button';
2776         if (typeof(b.listeners) == 'undefined') {
2777             b.listeners = { click : cb.createDelegate(this)  };
2778         }
2779         
2780         var btn = Roo.factory(b);
2781            
2782         btn.render(this.el.select('.modal-footer div').first());
2783         
2784         return btn;   
2785        
2786     },
2787     
2788     setDefaultButton : function(btn)
2789     {
2790         //this.el.select('.modal-footer').()
2791     },
2792     diff : false,
2793     
2794     resizeTo: function(w,h)
2795     {
2796         // skip.. ?? why??
2797         
2798         this.dialogEl.setWidth(w);
2799         if (this.diff === false) {
2800             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2801         }
2802         
2803         this.bodyEl.setHeight(h-this.diff);
2804         
2805         
2806     },
2807     setContentSize  : function(w, h)
2808     {
2809         
2810     },
2811     onButtonClick: function(btn,e)
2812     {
2813         //Roo.log([a,b,c]);
2814         this.fireEvent('btnclick', btn.name, e);
2815     },
2816      /**
2817      * Set the title of the Dialog
2818      * @param {String} str new Title
2819      */
2820     setTitle: function(str) {
2821         this.titleEl.dom.innerHTML = str;    
2822     },
2823     /**
2824      * Set the body of the Dialog
2825      * @param {String} str new Title
2826      */
2827     setBody: function(str) {
2828         this.bodyEl.dom.innerHTML = str;    
2829     },
2830     /**
2831      * Set the body of the Dialog using the template
2832      * @param {Obj} data - apply this data to the template and replace the body contents.
2833      */
2834     applyBody: function(obj)
2835     {
2836         if (!this.tmpl) {
2837             Roo.log("Error - using apply Body without a template");
2838             //code
2839         }
2840         this.tmpl.overwrite(this.bodyEl, obj);
2841     }
2842     
2843 });
2844
2845
2846 Roo.apply(Roo.bootstrap.Modal,  {
2847     /**
2848          * Button config that displays a single OK button
2849          * @type Object
2850          */
2851         OK :  [{
2852             name : 'ok',
2853             weight : 'primary',
2854             html : 'OK'
2855         }], 
2856         /**
2857          * Button config that displays Yes and No buttons
2858          * @type Object
2859          */
2860         YESNO : [
2861             {
2862                 name  : 'no',
2863                 html : 'No'
2864             },
2865             {
2866                 name  :'yes',
2867                 weight : 'primary',
2868                 html : 'Yes'
2869             }
2870         ],
2871         
2872         /**
2873          * Button config that displays OK and Cancel buttons
2874          * @type Object
2875          */
2876         OKCANCEL : [
2877             {
2878                name : 'cancel',
2879                 html : 'Cancel'
2880             },
2881             {
2882                 name : 'ok',
2883                 weight : 'primary',
2884                 html : 'OK'
2885             }
2886         ],
2887         /**
2888          * Button config that displays Yes, No and Cancel buttons
2889          * @type Object
2890          */
2891         YESNOCANCEL : [
2892             {
2893                 name : 'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             },
2897             {
2898                 name : 'no',
2899                 html : 'No'
2900             },
2901             {
2902                 name : 'cancel',
2903                 html : 'Cancel'
2904             }
2905         ]
2906 });
2907  
2908  /*
2909  * - LGPL
2910  *
2911  * messagebox - can be used as a replace
2912  * 
2913  */
2914 /**
2915  * @class Roo.MessageBox
2916  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2917  * Example usage:
2918  *<pre><code>
2919 // Basic alert:
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2921
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2924     if (btn == 'ok'){
2925         // process text value...
2926     }
2927 });
2928
2929 // Show a dialog using config options:
2930 Roo.Msg.show({
2931    title:'Save Changes?',
2932    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933    buttons: Roo.Msg.YESNOCANCEL,
2934    fn: processResult,
2935    animEl: 'elId'
2936 });
2937 </code></pre>
2938  * @singleton
2939  */
2940 Roo.bootstrap.MessageBox = function(){
2941     var dlg, opt, mask, waitTimer;
2942     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943     var buttons, activeTextEl, bwidth;
2944
2945     
2946     // private
2947     var handleButton = function(button){
2948         dlg.hide();
2949         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2950     };
2951
2952     // private
2953     var handleHide = function(){
2954         if(opt && opt.cls){
2955             dlg.el.removeClass(opt.cls);
2956         }
2957         //if(waitTimer){
2958         //    Roo.TaskMgr.stop(waitTimer);
2959         //    waitTimer = null;
2960         //}
2961     };
2962
2963     // private
2964     var updateButtons = function(b){
2965         var width = 0;
2966         if(!b){
2967             buttons["ok"].hide();
2968             buttons["cancel"].hide();
2969             buttons["yes"].hide();
2970             buttons["no"].hide();
2971             //dlg.footer.dom.style.display = 'none';
2972             return width;
2973         }
2974         dlg.footerEl.dom.style.display = '';
2975         for(var k in buttons){
2976             if(typeof buttons[k] != "function"){
2977                 if(b[k]){
2978                     buttons[k].show();
2979                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980                     width += buttons[k].el.getWidth()+15;
2981                 }else{
2982                     buttons[k].hide();
2983                 }
2984             }
2985         }
2986         return width;
2987     };
2988
2989     // private
2990     var handleEsc = function(d, k, e){
2991         if(opt && opt.closable !== false){
2992             dlg.hide();
2993         }
2994         if(e){
2995             e.stopEvent();
2996         }
2997     };
2998
2999     return {
3000         /**
3001          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002          * @return {Roo.BasicDialog} The BasicDialog element
3003          */
3004         getDialog : function(){
3005            if(!dlg){
3006                 dlg = new Roo.bootstrap.Modal( {
3007                     //draggable: true,
3008                     //resizable:false,
3009                     //constraintoviewport:false,
3010                     //fixedcenter:true,
3011                     //collapsible : false,
3012                     //shim:true,
3013                     //modal: true,
3014                   //  width:400,
3015                   //  height:100,
3016                     //buttonAlign:"center",
3017                     closeClick : function(){
3018                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3019                             handleButton("no");
3020                         }else{
3021                             handleButton("cancel");
3022                         }
3023                     }
3024                 });
3025                 dlg.render();
3026                 dlg.on("hide", handleHide);
3027                 mask = dlg.mask;
3028                 //dlg.addKeyListener(27, handleEsc);
3029                 buttons = {};
3030                 this.buttons = buttons;
3031                 var bt = this.buttonText;
3032                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3036                 //Roo.log(buttons);
3037                 bodyEl = dlg.bodyEl.createChild({
3038
3039                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040                         '<textarea class="roo-mb-textarea"></textarea>' +
3041                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3042                 });
3043                 msgEl = bodyEl.dom.firstChild;
3044                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045                 textboxEl.enableDisplayMode();
3046                 textboxEl.addKeyListener([10,13], function(){
3047                     if(dlg.isVisible() && opt && opt.buttons){
3048                         if(opt.buttons.ok){
3049                             handleButton("ok");
3050                         }else if(opt.buttons.yes){
3051                             handleButton("yes");
3052                         }
3053                     }
3054                 });
3055                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056                 textareaEl.enableDisplayMode();
3057                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058                 progressEl.enableDisplayMode();
3059                 var pf = progressEl.dom.firstChild;
3060                 if (pf) {
3061                     pp = Roo.get(pf.firstChild);
3062                     pp.setHeight(pf.offsetHeight);
3063                 }
3064                 
3065             }
3066             return dlg;
3067         },
3068
3069         /**
3070          * Updates the message box body text
3071          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072          * the XHTML-compliant non-breaking space character '&amp;#160;')
3073          * @return {Roo.MessageBox} This message box
3074          */
3075         updateText : function(text){
3076             if(!dlg.isVisible() && !opt.width){
3077                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3078             }
3079             msgEl.innerHTML = text || '&#160;';
3080       
3081             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3083             var w = Math.max(
3084                     Math.min(opt.width || cw , this.maxWidth), 
3085                     Math.max(opt.minWidth || this.minWidth, bwidth)
3086             );
3087             if(opt.prompt){
3088                 activeTextEl.setWidth(w);
3089             }
3090             if(dlg.isVisible()){
3091                 dlg.fixedcenter = false;
3092             }
3093             // to big, make it scroll. = But as usual stupid IE does not support
3094             // !important..
3095             
3096             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3099             } else {
3100                 bodyEl.dom.style.height = '';
3101                 bodyEl.dom.style.overflowY = '';
3102             }
3103             if (cw > w) {
3104                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3105             } else {
3106                 bodyEl.dom.style.overflowX = '';
3107             }
3108             
3109             dlg.setContentSize(w, bodyEl.getHeight());
3110             if(dlg.isVisible()){
3111                 dlg.fixedcenter = true;
3112             }
3113             return this;
3114         },
3115
3116         /**
3117          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3118          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121          * @return {Roo.MessageBox} This message box
3122          */
3123         updateProgress : function(value, text){
3124             if(text){
3125                 this.updateText(text);
3126             }
3127             if (pp) { // weird bug on my firefox - for some reason this is not defined
3128                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3129             }
3130             return this;
3131         },        
3132
3133         /**
3134          * Returns true if the message box is currently displayed
3135          * @return {Boolean} True if the message box is visible, else false
3136          */
3137         isVisible : function(){
3138             return dlg && dlg.isVisible();  
3139         },
3140
3141         /**
3142          * Hides the message box if it is displayed
3143          */
3144         hide : function(){
3145             if(this.isVisible()){
3146                 dlg.hide();
3147             }  
3148         },
3149
3150         /**
3151          * Displays a new message box, or reinitializes an existing message box, based on the config options
3152          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153          * The following config object properties are supported:
3154          * <pre>
3155 Property    Type             Description
3156 ----------  ---------------  ------------------------------------------------------------------------------------
3157 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3158                                    closes (defaults to undefined)
3159 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3162                                    progress and wait dialogs will ignore this property and always hide the
3163                                    close button as they can only be closed programmatically.
3164 cls               String           A custom CSS class to apply to the message box element
3165 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3166                                    displayed (defaults to 75)
3167 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3168                                    function will be btn (the name of the button that was clicked, if applicable,
3169                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3170                                    Progress and wait dialogs will ignore this option since they do not respond to
3171                                    user actions and can only be closed programmatically, so any required function
3172                                    should be called by the same code after it closes the dialog.
3173 icon              String           A CSS class that provides a background image to be used as an icon for
3174                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3176 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3177 modal             Boolean          False to allow user interaction with the page while the message box is
3178                                    displayed (defaults to true)
3179 msg               String           A string that will replace the existing message box body text (defaults
3180                                    to the XHTML-compliant non-breaking space character '&#160;')
3181 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3182 progress          Boolean          True to display a progress bar (defaults to false)
3183 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3186 title             String           The title text
3187 value             String           The string value to set into the active textbox element if displayed
3188 wait              Boolean          True to display a progress bar (defaults to false)
3189 width             Number           The width of the dialog in pixels
3190 </pre>
3191          *
3192          * Example usage:
3193          * <pre><code>
3194 Roo.Msg.show({
3195    title: 'Address',
3196    msg: 'Please enter your address:',
3197    width: 300,
3198    buttons: Roo.MessageBox.OKCANCEL,
3199    multiline: true,
3200    fn: saveAddress,
3201    animEl: 'addAddressBtn'
3202 });
3203 </code></pre>
3204          * @param {Object} config Configuration options
3205          * @return {Roo.MessageBox} This message box
3206          */
3207         show : function(options)
3208         {
3209             
3210             // this causes nightmares if you show one dialog after another
3211             // especially on callbacks..
3212              
3213             if(this.isVisible()){
3214                 
3215                 this.hide();
3216                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3218                 Roo.log("New Dialog Message:" +  options.msg )
3219                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3221                 
3222             }
3223             var d = this.getDialog();
3224             opt = options;
3225             d.setTitle(opt.title || "&#160;");
3226             d.closeEl.setDisplayed(opt.closable !== false);
3227             activeTextEl = textboxEl;
3228             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3229             if(opt.prompt){
3230                 if(opt.multiline){
3231                     textboxEl.hide();
3232                     textareaEl.show();
3233                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3234                         opt.multiline : this.defaultTextHeight);
3235                     activeTextEl = textareaEl;
3236                 }else{
3237                     textboxEl.show();
3238                     textareaEl.hide();
3239                 }
3240             }else{
3241                 textboxEl.hide();
3242                 textareaEl.hide();
3243             }
3244             progressEl.setDisplayed(opt.progress === true);
3245             this.updateProgress(0);
3246             activeTextEl.dom.value = opt.value || "";
3247             if(opt.prompt){
3248                 dlg.setDefaultButton(activeTextEl);
3249             }else{
3250                 var bs = opt.buttons;
3251                 var db = null;
3252                 if(bs && bs.ok){
3253                     db = buttons["ok"];
3254                 }else if(bs && bs.yes){
3255                     db = buttons["yes"];
3256                 }
3257                 dlg.setDefaultButton(db);
3258             }
3259             bwidth = updateButtons(opt.buttons);
3260             this.updateText(opt.msg);
3261             if(opt.cls){
3262                 d.el.addClass(opt.cls);
3263             }
3264             d.proxyDrag = opt.proxyDrag === true;
3265             d.modal = opt.modal !== false;
3266             d.mask = opt.modal !== false ? mask : false;
3267             if(!d.isVisible()){
3268                 // force it to the end of the z-index stack so it gets a cursor in FF
3269                 document.body.appendChild(dlg.el.dom);
3270                 d.animateTarget = null;
3271                 d.show(options.animEl);
3272             }
3273             return this;
3274         },
3275
3276         /**
3277          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3278          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279          * and closing the message box when the process is complete.
3280          * @param {String} title The title bar text
3281          * @param {String} msg The message box body text
3282          * @return {Roo.MessageBox} This message box
3283          */
3284         progress : function(title, msg){
3285             this.show({
3286                 title : title,
3287                 msg : msg,
3288                 buttons: false,
3289                 progress:true,
3290                 closable:false,
3291                 minWidth: this.minProgressWidth,
3292                 modal : true
3293             });
3294             return this;
3295         },
3296
3297         /**
3298          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299          * If a callback function is passed it will be called after the user clicks the button, and the
3300          * id of the button that was clicked will be passed as the only parameter to the callback
3301          * (could also be the top-right close button).
3302          * @param {String} title The title bar text
3303          * @param {String} msg The message box body text
3304          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305          * @param {Object} scope (optional) The scope of the callback function
3306          * @return {Roo.MessageBox} This message box
3307          */
3308         alert : function(title, msg, fn, scope){
3309             this.show({
3310                 title : title,
3311                 msg : msg,
3312                 buttons: this.OK,
3313                 fn: fn,
3314                 scope : scope,
3315                 modal : true
3316             });
3317             return this;
3318         },
3319
3320         /**
3321          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3322          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323          * You are responsible for closing the message box when the process is complete.
3324          * @param {String} msg The message box body text
3325          * @param {String} title (optional) The title bar text
3326          * @return {Roo.MessageBox} This message box
3327          */
3328         wait : function(msg, title){
3329             this.show({
3330                 title : title,
3331                 msg : msg,
3332                 buttons: false,
3333                 closable:false,
3334                 progress:true,
3335                 modal:true,
3336                 width:300,
3337                 wait:true
3338             });
3339             waitTimer = Roo.TaskMgr.start({
3340                 run: function(i){
3341                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3342                 },
3343                 interval: 1000
3344             });
3345             return this;
3346         },
3347
3348         /**
3349          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352          * @param {String} title The title bar text
3353          * @param {String} msg The message box body text
3354          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355          * @param {Object} scope (optional) The scope of the callback function
3356          * @return {Roo.MessageBox} This message box
3357          */
3358         confirm : function(title, msg, fn, scope){
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.YESNO,
3363                 fn: fn,
3364                 scope : scope,
3365                 modal : true
3366             });
3367             return this;
3368         },
3369
3370         /**
3371          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3373          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374          * (could also be the top-right close button) and the text that was entered will be passed as the two
3375          * parameters to the callback.
3376          * @param {String} title The title bar text
3377          * @param {String} msg The message box body text
3378          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379          * @param {Object} scope (optional) The scope of the callback function
3380          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382          * @return {Roo.MessageBox} This message box
3383          */
3384         prompt : function(title, msg, fn, scope, multiline){
3385             this.show({
3386                 title : title,
3387                 msg : msg,
3388                 buttons: this.OKCANCEL,
3389                 fn: fn,
3390                 minWidth:250,
3391                 scope : scope,
3392                 prompt:true,
3393                 multiline: multiline,
3394                 modal : true
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Button config that displays a single OK button
3401          * @type Object
3402          */
3403         OK : {ok:true},
3404         /**
3405          * Button config that displays Yes and No buttons
3406          * @type Object
3407          */
3408         YESNO : {yes:true, no:true},
3409         /**
3410          * Button config that displays OK and Cancel buttons
3411          * @type Object
3412          */
3413         OKCANCEL : {ok:true, cancel:true},
3414         /**
3415          * Button config that displays Yes, No and Cancel buttons
3416          * @type Object
3417          */
3418         YESNOCANCEL : {yes:true, no:true, cancel:true},
3419
3420         /**
3421          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3422          * @type Number
3423          */
3424         defaultTextHeight : 75,
3425         /**
3426          * The maximum width in pixels of the message box (defaults to 600)
3427          * @type Number
3428          */
3429         maxWidth : 600,
3430         /**
3431          * The minimum width in pixels of the message box (defaults to 100)
3432          * @type Number
3433          */
3434         minWidth : 100,
3435         /**
3436          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3437          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3438          * @type Number
3439          */
3440         minProgressWidth : 250,
3441         /**
3442          * An object containing the default button text strings that can be overriden for localized language support.
3443          * Supported properties are: ok, cancel, yes and no.
3444          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3445          * @type Object
3446          */
3447         buttonText : {
3448             ok : "OK",
3449             cancel : "Cancel",
3450             yes : "Yes",
3451             no : "No"
3452         }
3453     };
3454 }();
3455
3456 /**
3457  * Shorthand for {@link Roo.MessageBox}
3458  */
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3461 /*
3462  * - LGPL
3463  *
3464  * navbar
3465  * 
3466  */
3467
3468 /**
3469  * @class Roo.bootstrap.Navbar
3470  * @extends Roo.bootstrap.Component
3471  * Bootstrap Navbar class
3472
3473  * @constructor
3474  * Create a new Navbar
3475  * @param {Object} config The config object
3476  */
3477
3478
3479 Roo.bootstrap.Navbar = function(config){
3480     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3481     this.addEvents({
3482         // raw events
3483         /**
3484          * @event beforetoggle
3485          * Fire before toggle the menu
3486          * @param {Roo.EventObject} e
3487          */
3488         "beforetoggle" : true
3489     });
3490 };
3491
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3493     
3494     
3495    
3496     // private
3497     navItems : false,
3498     loadMask : false,
3499     
3500     
3501     getAutoCreate : function(){
3502         
3503         
3504         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3505         
3506     },
3507     
3508     initEvents :function ()
3509     {
3510         //Roo.log(this.el.select('.navbar-toggle',true));
3511         this.el.select('.navbar-toggle',true).on('click', function() {
3512             if(this.fireEvent('beforetoggle', this) !== false){
3513                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3514             }
3515             
3516         }, this);
3517         
3518         var mark = {
3519             tag: "div",
3520             cls:"x-dlg-mask"
3521         };
3522         
3523         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3524         
3525         var size = this.el.getSize();
3526         this.maskEl.setSize(size.width, size.height);
3527         this.maskEl.enableDisplayMode("block");
3528         this.maskEl.hide();
3529         
3530         if(this.loadMask){
3531             this.maskEl.show();
3532         }
3533     },
3534     
3535     
3536     getChildContainer : function()
3537     {
3538         if (this.el.select('.collapse').getCount()) {
3539             return this.el.select('.collapse',true).first();
3540         }
3541         
3542         return this.el;
3543     },
3544     
3545     mask : function()
3546     {
3547         this.maskEl.show();
3548     },
3549     
3550     unmask : function()
3551     {
3552         this.maskEl.hide();
3553     } 
3554     
3555     
3556     
3557     
3558 });
3559
3560
3561
3562  
3563
3564  /*
3565  * - LGPL
3566  *
3567  * navbar
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.NavSimplebar
3573  * @extends Roo.bootstrap.Navbar
3574  * Bootstrap Sidebar class
3575  *
3576  * @cfg {Boolean} inverse is inverted color
3577  * 
3578  * @cfg {String} type (nav | pills | tabs)
3579  * @cfg {Boolean} arrangement stacked | justified
3580  * @cfg {String} align (left | right) alignment
3581  * 
3582  * @cfg {Boolean} main (true|false) main nav bar? default false
3583  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3584  * 
3585  * @cfg {String} tag (header|footer|nav|div) default is nav 
3586
3587  * 
3588  * 
3589  * 
3590  * @constructor
3591  * Create a new Sidebar
3592  * @param {Object} config The config object
3593  */
3594
3595
3596 Roo.bootstrap.NavSimplebar = function(config){
3597     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3598 };
3599
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3601     
3602     inverse: false,
3603     
3604     type: false,
3605     arrangement: '',
3606     align : false,
3607     
3608     
3609     
3610     main : false,
3611     
3612     
3613     tag : false,
3614     
3615     
3616     getAutoCreate : function(){
3617         
3618         
3619         var cfg = {
3620             tag : this.tag || 'div',
3621             cls : 'navbar'
3622         };
3623           
3624         
3625         cfg.cn = [
3626             {
3627                 cls: 'nav',
3628                 tag : 'ul'
3629             }
3630         ];
3631         
3632          
3633         this.type = this.type || 'nav';
3634         if (['tabs','pills'].indexOf(this.type)!==-1) {
3635             cfg.cn[0].cls += ' nav-' + this.type
3636         
3637         
3638         } else {
3639             if (this.type!=='nav') {
3640                 Roo.log('nav type must be nav/tabs/pills')
3641             }
3642             cfg.cn[0].cls += ' navbar-nav'
3643         }
3644         
3645         
3646         
3647         
3648         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649             cfg.cn[0].cls += ' nav-' + this.arrangement;
3650         }
3651         
3652         
3653         if (this.align === 'right') {
3654             cfg.cn[0].cls += ' navbar-right';
3655         }
3656         
3657         if (this.inverse) {
3658             cfg.cls += ' navbar-inverse';
3659             
3660         }
3661         
3662         
3663         return cfg;
3664     
3665         
3666     }
3667     
3668     
3669     
3670 });
3671
3672
3673
3674  
3675
3676  
3677        /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavHeaderbar
3686  * @extends Roo.bootstrap.NavSimplebar
3687  * Bootstrap Sidebar class
3688  *
3689  * @cfg {String} brand what is brand
3690  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691  * @cfg {String} brand_href href of the brand
3692  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3693  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3696  * 
3697  * @constructor
3698  * Create a new Sidebar
3699  * @param {Object} config The config object
3700  */
3701
3702
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3705       
3706 };
3707
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3709     
3710     position: '',
3711     brand: '',
3712     brand_href: false,
3713     srButton : true,
3714     autohide : false,
3715     desktopCenter : false,
3716    
3717     
3718     getAutoCreate : function(){
3719         
3720         var   cfg = {
3721             tag: this.nav || 'nav',
3722             cls: 'navbar',
3723             role: 'navigation',
3724             cn: []
3725         };
3726         
3727         var cn = cfg.cn;
3728         if (this.desktopCenter) {
3729             cn.push({cls : 'container', cn : []});
3730             cn = cn[0].cn;
3731         }
3732         
3733         if(this.srButton){
3734             cn.push({
3735                 tag: 'div',
3736                 cls: 'navbar-header',
3737                 cn: [
3738                     {
3739                         tag: 'button',
3740                         type: 'button',
3741                         cls: 'navbar-toggle',
3742                         'data-toggle': 'collapse',
3743                         cn: [
3744                             {
3745                                 tag: 'span',
3746                                 cls: 'sr-only',
3747                                 html: 'Toggle navigation'
3748                             },
3749                             {
3750                                 tag: 'span',
3751                                 cls: 'icon-bar'
3752                             },
3753                             {
3754                                 tag: 'span',
3755                                 cls: 'icon-bar'
3756                             },
3757                             {
3758                                 tag: 'span',
3759                                 cls: 'icon-bar'
3760                             }
3761                         ]
3762                     }
3763                 ]
3764             });
3765         }
3766         
3767         cn.push({
3768             tag: 'div',
3769             cls: 'collapse navbar-collapse',
3770             cn : []
3771         });
3772         
3773         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3774         
3775         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776             cfg.cls += ' navbar-' + this.position;
3777             
3778             // tag can override this..
3779             
3780             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3781         }
3782         
3783         if (this.brand !== '') {
3784             cn[0].cn.push({
3785                 tag: 'a',
3786                 href: this.brand_href ? this.brand_href : '#',
3787                 cls: 'navbar-brand',
3788                 cn: [
3789                 this.brand
3790                 ]
3791             });
3792         }
3793         
3794         if(this.main){
3795             cfg.cls += ' main-nav';
3796         }
3797         
3798         
3799         return cfg;
3800
3801         
3802     },
3803     getHeaderChildContainer : function()
3804     {
3805         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806             return this.el.select('.navbar-header',true).first();
3807         }
3808         
3809         return this.getChildContainer();
3810     },
3811     
3812     
3813     initEvents : function()
3814     {
3815         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3816         
3817         if (this.autohide) {
3818             
3819             var prevScroll = 0;
3820             var ft = this.el;
3821             
3822             Roo.get(document).on('scroll',function(e) {
3823                 var ns = Roo.get(document).getScroll().top;
3824                 var os = prevScroll;
3825                 prevScroll = ns;
3826                 
3827                 if(ns > os){
3828                     ft.removeClass('slideDown');
3829                     ft.addClass('slideUp');
3830                     return;
3831                 }
3832                 ft.removeClass('slideUp');
3833                 ft.addClass('slideDown');
3834                  
3835               
3836           },this);
3837         }
3838     }    
3839     
3840 });
3841
3842
3843
3844  
3845
3846  /*
3847  * - LGPL
3848  *
3849  * navbar
3850  * 
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.NavSidebar
3855  * @extends Roo.bootstrap.Navbar
3856  * Bootstrap Sidebar class
3857  * 
3858  * @constructor
3859  * Create a new Sidebar
3860  * @param {Object} config The config object
3861  */
3862
3863
3864 Roo.bootstrap.NavSidebar = function(config){
3865     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3866 };
3867
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3869     
3870     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3871     
3872     getAutoCreate : function(){
3873         
3874         
3875         return  {
3876             tag: 'div',
3877             cls: 'sidebar sidebar-nav'
3878         };
3879     
3880         
3881     }
3882     
3883     
3884     
3885 });
3886
3887
3888
3889  
3890
3891  /*
3892  * - LGPL
3893  *
3894  * nav group
3895  * 
3896  */
3897
3898 /**
3899  * @class Roo.bootstrap.NavGroup
3900  * @extends Roo.bootstrap.Component
3901  * Bootstrap NavGroup class
3902  * @cfg {String} align (left|right)
3903  * @cfg {Boolean} inverse
3904  * @cfg {String} type (nav|pills|tab) default nav
3905  * @cfg {String} navId - reference Id for navbar.
3906
3907  * 
3908  * @constructor
3909  * Create a new nav group
3910  * @param {Object} config The config object
3911  */
3912
3913 Roo.bootstrap.NavGroup = function(config){
3914     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3915     this.navItems = [];
3916    
3917     Roo.bootstrap.NavGroup.register(this);
3918      this.addEvents({
3919         /**
3920              * @event changed
3921              * Fires when the active item changes
3922              * @param {Roo.bootstrap.NavGroup} this
3923              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3925          */
3926         'changed': true
3927      });
3928     
3929 };
3930
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3932     
3933     align: '',
3934     inverse: false,
3935     form: false,
3936     type: 'nav',
3937     navId : '',
3938     // private
3939     
3940     navItems : false, 
3941     
3942     getAutoCreate : function()
3943     {
3944         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3945         
3946         cfg = {
3947             tag : 'ul',
3948             cls: 'nav' 
3949         };
3950         
3951         if (['tabs','pills'].indexOf(this.type)!==-1) {
3952             cfg.cls += ' nav-' + this.type
3953         } else {
3954             if (this.type!=='nav') {
3955                 Roo.log('nav type must be nav/tabs/pills')
3956             }
3957             cfg.cls += ' navbar-nav'
3958         }
3959         
3960         if (this.parent().sidebar) {
3961             cfg = {
3962                 tag: 'ul',
3963                 cls: 'dashboard-menu sidebar-menu'
3964             };
3965             
3966             return cfg;
3967         }
3968         
3969         if (this.form === true) {
3970             cfg = {
3971                 tag: 'form',
3972                 cls: 'navbar-form'
3973             };
3974             
3975             if (this.align === 'right') {
3976                 cfg.cls += ' navbar-right';
3977             } else {
3978                 cfg.cls += ' navbar-left';
3979             }
3980         }
3981         
3982         if (this.align === 'right') {
3983             cfg.cls += ' navbar-right';
3984         }
3985         
3986         if (this.inverse) {
3987             cfg.cls += ' navbar-inverse';
3988             
3989         }
3990         
3991         
3992         return cfg;
3993     },
3994     /**
3995     * sets the active Navigation item
3996     * @param {Roo.bootstrap.NavItem} the new current navitem
3997     */
3998     setActiveItem : function(item)
3999     {
4000         var prev = false;
4001         Roo.each(this.navItems, function(v){
4002             if (v == item) {
4003                 return ;
4004             }
4005             if (v.isActive()) {
4006                 v.setActive(false, true);
4007                 prev = v;
4008                 
4009             }
4010             
4011         });
4012
4013         item.setActive(true, true);
4014         this.fireEvent('changed', this, item, prev);
4015         
4016         
4017     },
4018     /**
4019     * gets the active Navigation item
4020     * @return {Roo.bootstrap.NavItem} the current navitem
4021     */
4022     getActive : function()
4023     {
4024         
4025         var prev = false;
4026         Roo.each(this.navItems, function(v){
4027             
4028             if (v.isActive()) {
4029                 prev = v;
4030                 
4031             }
4032             
4033         });
4034         return prev;
4035     },
4036     
4037     indexOfNav : function()
4038     {
4039         
4040         var prev = false;
4041         Roo.each(this.navItems, function(v,i){
4042             
4043             if (v.isActive()) {
4044                 prev = i;
4045                 
4046             }
4047             
4048         });
4049         return prev;
4050     },
4051     /**
4052     * adds a Navigation item
4053     * @param {Roo.bootstrap.NavItem} the navitem to add
4054     */
4055     addItem : function(cfg)
4056     {
4057         var cn = new Roo.bootstrap.NavItem(cfg);
4058         this.register(cn);
4059         cn.parentId = this.id;
4060         cn.onRender(this.el, null);
4061         return cn;
4062     },
4063     /**
4064     * register a Navigation item
4065     * @param {Roo.bootstrap.NavItem} the navitem to add
4066     */
4067     register : function(item)
4068     {
4069         this.navItems.push( item);
4070         item.navId = this.navId;
4071     
4072     },
4073     
4074     /**
4075     * clear all the Navigation item
4076     */
4077    
4078     clearAll : function()
4079     {
4080         this.navItems = [];
4081         this.el.dom.innerHTML = '';
4082     },
4083     
4084     getNavItem: function(tabId)
4085     {
4086         var ret = false;
4087         Roo.each(this.navItems, function(e) {
4088             if (e.tabId == tabId) {
4089                ret =  e;
4090                return false;
4091             }
4092             return true;
4093             
4094         });
4095         return ret;
4096     },
4097     
4098     setActiveNext : function()
4099     {
4100         var i = this.indexOfNav(this.getActive());
4101         if (i > this.navItems.length) {
4102             return;
4103         }
4104         this.setActiveItem(this.navItems[i+1]);
4105     },
4106     setActivePrev : function()
4107     {
4108         var i = this.indexOfNav(this.getActive());
4109         if (i  < 1) {
4110             return;
4111         }
4112         this.setActiveItem(this.navItems[i-1]);
4113     },
4114     clearWasActive : function(except) {
4115         Roo.each(this.navItems, function(e) {
4116             if (e.tabId != except.tabId && e.was_active) {
4117                e.was_active = false;
4118                return false;
4119             }
4120             return true;
4121             
4122         });
4123     },
4124     getWasActive : function ()
4125     {
4126         var r = false;
4127         Roo.each(this.navItems, function(e) {
4128             if (e.was_active) {
4129                r = e;
4130                return false;
4131             }
4132             return true;
4133             
4134         });
4135         return r;
4136     }
4137     
4138     
4139 });
4140
4141  
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4143     
4144     groups: {},
4145      /**
4146     * register a Navigation Group
4147     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4148     */
4149     register : function(navgrp)
4150     {
4151         this.groups[navgrp.navId] = navgrp;
4152         
4153     },
4154     /**
4155     * fetch a Navigation Group based on the navigation ID
4156     * @param {string} the navgroup to add
4157     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4158     */
4159     get: function(navId) {
4160         if (typeof(this.groups[navId]) == 'undefined') {
4161             return false;
4162             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4163         }
4164         return this.groups[navId] ;
4165     }
4166     
4167     
4168     
4169 });
4170
4171  /*
4172  * - LGPL
4173  *
4174  * row
4175  * 
4176  */
4177
4178 /**
4179  * @class Roo.bootstrap.NavItem
4180  * @extends Roo.bootstrap.Component
4181  * Bootstrap Navbar.NavItem class
4182  * @cfg {String} href  link to
4183  * @cfg {String} html content of button
4184  * @cfg {String} badge text inside badge
4185  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186  * @cfg {String} glyphicon name of glyphicon
4187  * @cfg {String} icon name of font awesome icon
4188  * @cfg {Boolean} active Is item active
4189  * @cfg {Boolean} disabled Is item disabled
4190  
4191  * @cfg {Boolean} preventDefault (true | false) default false
4192  * @cfg {String} tabId the tab that this item activates.
4193  * @cfg {String} tagtype (a|span) render as a href or span?
4194  * @cfg {Boolean} animateRef (true|false) link to element default false  
4195   
4196  * @constructor
4197  * Create a new Navbar Item
4198  * @param {Object} config The config object
4199  */
4200 Roo.bootstrap.NavItem = function(config){
4201     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * The raw click event for the entire grid.
4207          * @param {Roo.EventObject} e
4208          */
4209         "click" : true,
4210          /**
4211             * @event changed
4212             * Fires when the active item active state changes
4213             * @param {Roo.bootstrap.NavItem} this
4214             * @param {boolean} state the new state
4215              
4216          */
4217         'changed': true,
4218         /**
4219             * @event scrollto
4220             * Fires when scroll to element
4221             * @param {Roo.bootstrap.NavItem} this
4222             * @param {Object} options
4223             * @param {Roo.EventObject} e
4224              
4225          */
4226         'scrollto': true
4227     });
4228    
4229 };
4230
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4232     
4233     href: false,
4234     html: '',
4235     badge: '',
4236     icon: false,
4237     glyphicon: false,
4238     active: false,
4239     preventDefault : false,
4240     tabId : false,
4241     tagtype : 'a',
4242     disabled : false,
4243     animateRef : false,
4244     was_active : false,
4245     
4246     getAutoCreate : function(){
4247          
4248         var cfg = {
4249             tag: 'li',
4250             cls: 'nav-item'
4251             
4252         };
4253         
4254         if (this.active) {
4255             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4256         }
4257         if (this.disabled) {
4258             cfg.cls += ' disabled';
4259         }
4260         
4261         if (this.href || this.html || this.glyphicon || this.icon) {
4262             cfg.cn = [
4263                 {
4264                     tag: this.tagtype,
4265                     href : this.href || "#",
4266                     html: this.html || ''
4267                 }
4268             ];
4269             
4270             if (this.icon) {
4271                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4272             }
4273
4274             if(this.glyphicon) {
4275                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4276             }
4277             
4278             if (this.menu) {
4279                 
4280                 cfg.cn[0].html += " <span class='caret'></span>";
4281              
4282             }
4283             
4284             if (this.badge !== '') {
4285                  
4286                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4287             }
4288         }
4289         
4290         
4291         
4292         return cfg;
4293     },
4294     initEvents: function() 
4295     {
4296         if (typeof (this.menu) != 'undefined') {
4297             this.menu.parentType = this.xtype;
4298             this.menu.triggerEl = this.el;
4299             this.menu = this.addxtype(Roo.apply({}, this.menu));
4300         }
4301         
4302         this.el.select('a',true).on('click', this.onClick, this);
4303         
4304         if(this.tagtype == 'span'){
4305             this.el.select('span',true).on('click', this.onClick, this);
4306         }
4307        
4308         // at this point parent should be available..
4309         this.parent().register(this);
4310     },
4311     
4312     onClick : function(e)
4313     {
4314         if (e.getTarget('.dropdown-menu-item')) {
4315             // did you click on a menu itemm.... - then don't trigger onclick..
4316             return;
4317         }
4318         
4319         if(
4320                 this.preventDefault || 
4321                 this.href == '#' 
4322         ){
4323             Roo.log("NavItem - prevent Default?");
4324             e.preventDefault();
4325         }
4326         
4327         if (this.disabled) {
4328             return;
4329         }
4330         
4331         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332         if (tg && tg.transition) {
4333             Roo.log("waiting for the transitionend");
4334             return;
4335         }
4336         
4337         
4338         
4339         //Roo.log("fire event clicked");
4340         if(this.fireEvent('click', this, e) === false){
4341             return;
4342         };
4343         
4344         if(this.tagtype == 'span'){
4345             return;
4346         }
4347         
4348         //Roo.log(this.href);
4349         var ael = this.el.select('a',true).first();
4350         //Roo.log(ael);
4351         
4352         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355                 return; // ignore... - it's a 'hash' to another page.
4356             }
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359             this.scrollToElement(e);
4360         }
4361         
4362         
4363         var p =  this.parent();
4364    
4365         if (['tabs','pills'].indexOf(p.type)!==-1) {
4366             if (typeof(p.setActiveItem) !== 'undefined') {
4367                 p.setActiveItem(this);
4368             }
4369         }
4370         
4371         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373             // remove the collapsed menu expand...
4374             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4375         }
4376     },
4377     
4378     isActive: function () {
4379         return this.active
4380     },
4381     setActive : function(state, fire, is_was_active)
4382     {
4383         if (this.active && !state && this.navId) {
4384             this.was_active = true;
4385             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4386             if (nv) {
4387                 nv.clearWasActive(this);
4388             }
4389             
4390         }
4391         this.active = state;
4392         
4393         if (!state ) {
4394             this.el.removeClass('active');
4395         } else if (!this.el.hasClass('active')) {
4396             this.el.addClass('active');
4397         }
4398         if (fire) {
4399             this.fireEvent('changed', this, state);
4400         }
4401         
4402         // show a panel if it's registered and related..
4403         
4404         if (!this.navId || !this.tabId || !state || is_was_active) {
4405             return;
4406         }
4407         
4408         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4409         if (!tg) {
4410             return;
4411         }
4412         var pan = tg.getPanelByName(this.tabId);
4413         if (!pan) {
4414             return;
4415         }
4416         // if we can not flip to new panel - go back to old nav highlight..
4417         if (false == tg.showPanel(pan)) {
4418             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4419             if (nv) {
4420                 var onav = nv.getWasActive();
4421                 if (onav) {
4422                     onav.setActive(true, false, true);
4423                 }
4424             }
4425             
4426         }
4427         
4428         
4429         
4430     },
4431      // this should not be here...
4432     setDisabled : function(state)
4433     {
4434         this.disabled = state;
4435         if (!state ) {
4436             this.el.removeClass('disabled');
4437         } else if (!this.el.hasClass('disabled')) {
4438             this.el.addClass('disabled');
4439         }
4440         
4441     },
4442     
4443     /**
4444      * Fetch the element to display the tooltip on.
4445      * @return {Roo.Element} defaults to this.el
4446      */
4447     tooltipEl : function()
4448     {
4449         return this.el.select('' + this.tagtype + '', true).first();
4450     },
4451     
4452     scrollToElement : function(e)
4453     {
4454         var c = document.body;
4455         
4456         /*
4457          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4458          */
4459         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460             c = document.documentElement;
4461         }
4462         
4463         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4464         
4465         if(!target){
4466             return;
4467         }
4468
4469         var o = target.calcOffsetsTo(c);
4470         
4471         var options = {
4472             target : target,
4473             value : o[1]
4474         };
4475         
4476         this.fireEvent('scrollto', this, options, e);
4477         
4478         Roo.get(c).scrollTo('top', options.value, true);
4479         
4480         return;
4481     }
4482 });
4483  
4484
4485  /*
4486  * - LGPL
4487  *
4488  * sidebar item
4489  *
4490  *  li
4491  *    <span> icon </span>
4492  *    <span> text </span>
4493  *    <span>badge </span>
4494  */
4495
4496 /**
4497  * @class Roo.bootstrap.NavSidebarItem
4498  * @extends Roo.bootstrap.NavItem
4499  * Bootstrap Navbar.NavSidebarItem class
4500  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501  * {bool} open is the menu open
4502  * @constructor
4503  * Create a new Navbar Button
4504  * @param {Object} config The config object
4505  */
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4508     this.addEvents({
4509         // raw events
4510         /**
4511          * @event click
4512          * The raw click event for the entire grid.
4513          * @param {Roo.EventObject} e
4514          */
4515         "click" : true,
4516          /**
4517             * @event changed
4518             * Fires when the active item active state changes
4519             * @param {Roo.bootstrap.NavSidebarItem} this
4520             * @param {boolean} state the new state
4521              
4522          */
4523         'changed': true
4524     });
4525    
4526 };
4527
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4529     
4530     badgeWeight : 'default',
4531     
4532     open: false,
4533     
4534     getAutoCreate : function(){
4535         
4536         
4537         var a = {
4538                 tag: 'a',
4539                 href : this.href || '#',
4540                 cls: '',
4541                 html : '',
4542                 cn : []
4543         };
4544         var cfg = {
4545             tag: 'li',
4546             cls: '',
4547             cn: [ a ]
4548         };
4549         var span = {
4550             tag: 'span',
4551             html : this.html || ''
4552         };
4553         
4554         
4555         if (this.active) {
4556             cfg.cls += ' active';
4557         }
4558         
4559         if (this.disabled) {
4560             cfg.cls += ' disabled';
4561         }
4562         if (this.open) {
4563             cfg.cls += ' open x-open';
4564         }
4565         // left icon..
4566         if (this.glyphicon || this.icon) {
4567             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4568             a.cn.push({ tag : 'i', cls : c }) ;
4569         }
4570         // html..
4571         a.cn.push(span);
4572         // then badge..
4573         if (this.badge !== '') {
4574             
4575             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4576         }
4577         // fi
4578         if (this.menu) {
4579             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580             a.cls += 'dropdown-toggle treeview' ;
4581         }
4582         
4583         return cfg;
4584          
4585            
4586     },
4587     
4588     initEvents : function()
4589     { 
4590         if (typeof (this.menu) != 'undefined') {
4591             this.menu.parentType = this.xtype;
4592             this.menu.triggerEl = this.el;
4593             this.menu = this.addxtype(Roo.apply({}, this.menu));
4594         }
4595         
4596         this.el.on('click', this.onClick, this);
4597        
4598     
4599         if(this.badge !== ''){
4600  
4601             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4602         }
4603         
4604     },
4605     
4606     onClick : function(e)
4607     {
4608         if(this.disabled){
4609             e.preventDefault();
4610             return;
4611         }
4612         
4613         if(this.preventDefault){
4614             e.preventDefault();
4615         }
4616         
4617         this.fireEvent('click', this);
4618     },
4619     
4620     disable : function()
4621     {
4622         this.setDisabled(true);
4623     },
4624     
4625     enable : function()
4626     {
4627         this.setDisabled(false);
4628     },
4629     
4630     setDisabled : function(state)
4631     {
4632         if(this.disabled == state){
4633             return;
4634         }
4635         
4636         this.disabled = state;
4637         
4638         if (state) {
4639             this.el.addClass('disabled');
4640             return;
4641         }
4642         
4643         this.el.removeClass('disabled');
4644         
4645         return;
4646     },
4647     
4648     setActive : function(state)
4649     {
4650         if(this.active == state){
4651             return;
4652         }
4653         
4654         this.active = state;
4655         
4656         if (state) {
4657             this.el.addClass('active');
4658             return;
4659         }
4660         
4661         this.el.removeClass('active');
4662         
4663         return;
4664     },
4665     
4666     isActive: function () 
4667     {
4668         return this.active;
4669     },
4670     
4671     setBadge : function(str)
4672     {
4673         if(!this.badgeEl){
4674             return;
4675         }
4676         
4677         this.badgeEl.dom.innerHTML = str;
4678     }
4679     
4680    
4681      
4682  
4683 });
4684  
4685
4686  /*
4687  * - LGPL
4688  *
4689  * row
4690  * 
4691  */
4692
4693 /**
4694  * @class Roo.bootstrap.Row
4695  * @extends Roo.bootstrap.Component
4696  * Bootstrap Row class (contains columns...)
4697  * 
4698  * @constructor
4699  * Create a new Row
4700  * @param {Object} config The config object
4701  */
4702
4703 Roo.bootstrap.Row = function(config){
4704     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4705 };
4706
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4708     
4709     getAutoCreate : function(){
4710        return {
4711             cls: 'row clearfix'
4712        };
4713     }
4714     
4715     
4716 });
4717
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * element
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Element
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Element class
4731  * @cfg {String} html contents of the element
4732  * @cfg {String} tag tag of the element
4733  * @cfg {String} cls class of the element
4734  * @cfg {Boolean} preventDefault (true|false) default false
4735  * @cfg {Boolean} clickable (true|false) default false
4736  * 
4737  * @constructor
4738  * Create a new Element
4739  * @param {Object} config The config object
4740  */
4741
4742 Roo.bootstrap.Element = function(config){
4743     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4744     
4745     this.addEvents({
4746         // raw events
4747         /**
4748          * @event click
4749          * When a element is chick
4750          * @param {Roo.bootstrap.Element} this
4751          * @param {Roo.EventObject} e
4752          */
4753         "click" : true
4754     });
4755 };
4756
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4758     
4759     tag: 'div',
4760     cls: '',
4761     html: '',
4762     preventDefault: false, 
4763     clickable: false,
4764     
4765     getAutoCreate : function(){
4766         
4767         var cfg = {
4768             tag: this.tag,
4769             cls: this.cls,
4770             html: this.html
4771         };
4772         
4773         return cfg;
4774     },
4775     
4776     initEvents: function() 
4777     {
4778         Roo.bootstrap.Element.superclass.initEvents.call(this);
4779         
4780         if(this.clickable){
4781             this.el.on('click', this.onClick, this);
4782         }
4783         
4784     },
4785     
4786     onClick : function(e)
4787     {
4788         if(this.preventDefault){
4789             e.preventDefault();
4790         }
4791         
4792         this.fireEvent('click', this, e);
4793     },
4794     
4795     getValue : function()
4796     {
4797         return this.el.dom.innerHTML;
4798     },
4799     
4800     setValue : function(value)
4801     {
4802         this.el.dom.innerHTML = value;
4803     }
4804    
4805 });
4806
4807  
4808
4809  /*
4810  * - LGPL
4811  *
4812  * pagination
4813  * 
4814  */
4815
4816 /**
4817  * @class Roo.bootstrap.Pagination
4818  * @extends Roo.bootstrap.Component
4819  * Bootstrap Pagination class
4820  * @cfg {String} size xs | sm | md | lg
4821  * @cfg {Boolean} inverse false | true
4822  * 
4823  * @constructor
4824  * Create a new Pagination
4825  * @param {Object} config The config object
4826  */
4827
4828 Roo.bootstrap.Pagination = function(config){
4829     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4830 };
4831
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4833     
4834     cls: false,
4835     size: false,
4836     inverse: false,
4837     
4838     getAutoCreate : function(){
4839         var cfg = {
4840             tag: 'ul',
4841                 cls: 'pagination'
4842         };
4843         if (this.inverse) {
4844             cfg.cls += ' inverse';
4845         }
4846         if (this.html) {
4847             cfg.html=this.html;
4848         }
4849         if (this.cls) {
4850             cfg.cls += " " + this.cls;
4851         }
4852         return cfg;
4853     }
4854    
4855 });
4856
4857  
4858
4859  /*
4860  * - LGPL
4861  *
4862  * Pagination item
4863  * 
4864  */
4865
4866
4867 /**
4868  * @class Roo.bootstrap.PaginationItem
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap PaginationItem class
4871  * @cfg {String} html text
4872  * @cfg {String} href the link
4873  * @cfg {Boolean} preventDefault (true | false) default true
4874  * @cfg {Boolean} active (true | false) default false
4875  * @cfg {Boolean} disabled default false
4876  * 
4877  * 
4878  * @constructor
4879  * Create a new PaginationItem
4880  * @param {Object} config The config object
4881  */
4882
4883
4884 Roo.bootstrap.PaginationItem = function(config){
4885     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4886     this.addEvents({
4887         // raw events
4888         /**
4889          * @event click
4890          * The raw click event for the entire grid.
4891          * @param {Roo.EventObject} e
4892          */
4893         "click" : true
4894     });
4895 };
4896
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4898     
4899     href : false,
4900     html : false,
4901     preventDefault: true,
4902     active : false,
4903     cls : false,
4904     disabled: false,
4905     
4906     getAutoCreate : function(){
4907         var cfg= {
4908             tag: 'li',
4909             cn: [
4910                 {
4911                     tag : 'a',
4912                     href : this.href ? this.href : '#',
4913                     html : this.html ? this.html : ''
4914                 }
4915             ]
4916         };
4917         
4918         if(this.cls){
4919             cfg.cls = this.cls;
4920         }
4921         
4922         if(this.disabled){
4923             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4924         }
4925         
4926         if(this.active){
4927             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4928         }
4929         
4930         return cfg;
4931     },
4932     
4933     initEvents: function() {
4934         
4935         this.el.on('click', this.onClick, this);
4936         
4937     },
4938     onClick : function(e)
4939     {
4940         Roo.log('PaginationItem on click ');
4941         if(this.preventDefault){
4942             e.preventDefault();
4943         }
4944         
4945         if(this.disabled){
4946             return;
4947         }
4948         
4949         this.fireEvent('click', this, e);
4950     }
4951    
4952 });
4953
4954  
4955
4956  /*
4957  * - LGPL
4958  *
4959  * slider
4960  * 
4961  */
4962
4963
4964 /**
4965  * @class Roo.bootstrap.Slider
4966  * @extends Roo.bootstrap.Component
4967  * Bootstrap Slider class
4968  *    
4969  * @constructor
4970  * Create a new Slider
4971  * @param {Object} config The config object
4972  */
4973
4974 Roo.bootstrap.Slider = function(config){
4975     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4976 };
4977
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4979     
4980     getAutoCreate : function(){
4981         
4982         var cfg = {
4983             tag: 'div',
4984             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4985             cn: [
4986                 {
4987                     tag: 'a',
4988                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4989                 }
4990             ]
4991         };
4992         
4993         return cfg;
4994     }
4995    
4996 });
4997
4998  /*
4999  * Based on:
5000  * Ext JS Library 1.1.1
5001  * Copyright(c) 2006-2007, Ext JS, LLC.
5002  *
5003  * Originally Released Under LGPL - original licence link has changed is not relivant.
5004  *
5005  * Fork - LGPL
5006  * <script type="text/javascript">
5007  */
5008  
5009
5010 /**
5011  * @class Roo.grid.ColumnModel
5012  * @extends Roo.util.Observable
5013  * This is the default implementation of a ColumnModel used by the Grid. It defines
5014  * the columns in the grid.
5015  * <br>Usage:<br>
5016  <pre><code>
5017  var colModel = new Roo.grid.ColumnModel([
5018         {header: "Ticker", width: 60, sortable: true, locked: true},
5019         {header: "Company Name", width: 150, sortable: true},
5020         {header: "Market Cap.", width: 100, sortable: true},
5021         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022         {header: "Employees", width: 100, sortable: true, resizable: false}
5023  ]);
5024  </code></pre>
5025  * <p>
5026  
5027  * The config options listed for this class are options which may appear in each
5028  * individual column definition.
5029  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5030  * @constructor
5031  * @param {Object} config An Array of column config objects. See this class's
5032  * config objects for details.
5033 */
5034 Roo.grid.ColumnModel = function(config){
5035         /**
5036      * The config passed into the constructor
5037      */
5038     this.config = config;
5039     this.lookup = {};
5040
5041     // if no id, create one
5042     // if the column does not have a dataIndex mapping,
5043     // map it to the order it is in the config
5044     for(var i = 0, len = config.length; i < len; i++){
5045         var c = config[i];
5046         if(typeof c.dataIndex == "undefined"){
5047             c.dataIndex = i;
5048         }
5049         if(typeof c.renderer == "string"){
5050             c.renderer = Roo.util.Format[c.renderer];
5051         }
5052         if(typeof c.id == "undefined"){
5053             c.id = Roo.id();
5054         }
5055         if(c.editor && c.editor.xtype){
5056             c.editor  = Roo.factory(c.editor, Roo.grid);
5057         }
5058         if(c.editor && c.editor.isFormField){
5059             c.editor = new Roo.grid.GridEditor(c.editor);
5060         }
5061         this.lookup[c.id] = c;
5062     }
5063
5064     /**
5065      * The width of columns which have no width specified (defaults to 100)
5066      * @type Number
5067      */
5068     this.defaultWidth = 100;
5069
5070     /**
5071      * Default sortable of columns which have no sortable specified (defaults to false)
5072      * @type Boolean
5073      */
5074     this.defaultSortable = false;
5075
5076     this.addEvents({
5077         /**
5078              * @event widthchange
5079              * Fires when the width of a column changes.
5080              * @param {ColumnModel} this
5081              * @param {Number} columnIndex The column index
5082              * @param {Number} newWidth The new width
5083              */
5084             "widthchange": true,
5085         /**
5086              * @event headerchange
5087              * Fires when the text of a header changes.
5088              * @param {ColumnModel} this
5089              * @param {Number} columnIndex The column index
5090              * @param {Number} newText The new header text
5091              */
5092             "headerchange": true,
5093         /**
5094              * @event hiddenchange
5095              * Fires when a column is hidden or "unhidden".
5096              * @param {ColumnModel} this
5097              * @param {Number} columnIndex The column index
5098              * @param {Boolean} hidden true if hidden, false otherwise
5099              */
5100             "hiddenchange": true,
5101             /**
5102          * @event columnmoved
5103          * Fires when a column is moved.
5104          * @param {ColumnModel} this
5105          * @param {Number} oldIndex
5106          * @param {Number} newIndex
5107          */
5108         "columnmoved" : true,
5109         /**
5110          * @event columlockchange
5111          * Fires when a column's locked state is changed
5112          * @param {ColumnModel} this
5113          * @param {Number} colIndex
5114          * @param {Boolean} locked true if locked
5115          */
5116         "columnlockchange" : true
5117     });
5118     Roo.grid.ColumnModel.superclass.constructor.call(this);
5119 };
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5121     /**
5122      * @cfg {String} header The header text to display in the Grid view.
5123      */
5124     /**
5125      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127      * specified, the column's index is used as an index into the Record's data Array.
5128      */
5129     /**
5130      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5132      */
5133     /**
5134      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135      * Defaults to the value of the {@link #defaultSortable} property.
5136      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5137      */
5138     /**
5139      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5140      */
5141     /**
5142      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5143      */
5144     /**
5145      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5146      */
5147     /**
5148      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5149      */
5150     /**
5151      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5155      */
5156        /**
5157      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5158      */
5159     /**
5160      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5161      */
5162     /**
5163      * @cfg {String} cursor (Optional)
5164      */
5165     /**
5166      * @cfg {String} tooltip (Optional)
5167      */
5168     /**
5169      * @cfg {Number} xs (Optional)
5170      */
5171     /**
5172      * @cfg {Number} sm (Optional)
5173      */
5174     /**
5175      * @cfg {Number} md (Optional)
5176      */
5177     /**
5178      * @cfg {Number} lg (Optional)
5179      */
5180     /**
5181      * Returns the id of the column at the specified index.
5182      * @param {Number} index The column index
5183      * @return {String} the id
5184      */
5185     getColumnId : function(index){
5186         return this.config[index].id;
5187     },
5188
5189     /**
5190      * Returns the column for a specified id.
5191      * @param {String} id The column id
5192      * @return {Object} the column
5193      */
5194     getColumnById : function(id){
5195         return this.lookup[id];
5196     },
5197
5198     
5199     /**
5200      * Returns the column for a specified dataIndex.
5201      * @param {String} dataIndex The column dataIndex
5202      * @return {Object|Boolean} the column or false if not found
5203      */
5204     getColumnByDataIndex: function(dataIndex){
5205         var index = this.findColumnIndex(dataIndex);
5206         return index > -1 ? this.config[index] : false;
5207     },
5208     
5209     /**
5210      * Returns the index for a specified column id.
5211      * @param {String} id The column id
5212      * @return {Number} the index, or -1 if not found
5213      */
5214     getIndexById : function(id){
5215         for(var i = 0, len = this.config.length; i < len; i++){
5216             if(this.config[i].id == id){
5217                 return i;
5218             }
5219         }
5220         return -1;
5221     },
5222     
5223     /**
5224      * Returns the index for a specified column dataIndex.
5225      * @param {String} dataIndex The column dataIndex
5226      * @return {Number} the index, or -1 if not found
5227      */
5228     
5229     findColumnIndex : function(dataIndex){
5230         for(var i = 0, len = this.config.length; i < len; i++){
5231             if(this.config[i].dataIndex == dataIndex){
5232                 return i;
5233             }
5234         }
5235         return -1;
5236     },
5237     
5238     
5239     moveColumn : function(oldIndex, newIndex){
5240         var c = this.config[oldIndex];
5241         this.config.splice(oldIndex, 1);
5242         this.config.splice(newIndex, 0, c);
5243         this.dataMap = null;
5244         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5245     },
5246
5247     isLocked : function(colIndex){
5248         return this.config[colIndex].locked === true;
5249     },
5250
5251     setLocked : function(colIndex, value, suppressEvent){
5252         if(this.isLocked(colIndex) == value){
5253             return;
5254         }
5255         this.config[colIndex].locked = value;
5256         if(!suppressEvent){
5257             this.fireEvent("columnlockchange", this, colIndex, value);
5258         }
5259     },
5260
5261     getTotalLockedWidth : function(){
5262         var totalWidth = 0;
5263         for(var i = 0; i < this.config.length; i++){
5264             if(this.isLocked(i) && !this.isHidden(i)){
5265                 this.totalWidth += this.getColumnWidth(i);
5266             }
5267         }
5268         return totalWidth;
5269     },
5270
5271     getLockedCount : function(){
5272         for(var i = 0, len = this.config.length; i < len; i++){
5273             if(!this.isLocked(i)){
5274                 return i;
5275             }
5276         }
5277         
5278         return this.config.length;
5279     },
5280
5281     /**
5282      * Returns the number of columns.
5283      * @return {Number}
5284      */
5285     getColumnCount : function(visibleOnly){
5286         if(visibleOnly === true){
5287             var c = 0;
5288             for(var i = 0, len = this.config.length; i < len; i++){
5289                 if(!this.isHidden(i)){
5290                     c++;
5291                 }
5292             }
5293             return c;
5294         }
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300      * @param {Function} fn
5301      * @param {Object} scope (optional)
5302      * @return {Array} result
5303      */
5304     getColumnsBy : function(fn, scope){
5305         var r = [];
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             var c = this.config[i];
5308             if(fn.call(scope||this, c, i) === true){
5309                 r[r.length] = c;
5310             }
5311         }
5312         return r;
5313     },
5314
5315     /**
5316      * Returns true if the specified column is sortable.
5317      * @param {Number} col The column index
5318      * @return {Boolean}
5319      */
5320     isSortable : function(col){
5321         if(typeof this.config[col].sortable == "undefined"){
5322             return this.defaultSortable;
5323         }
5324         return this.config[col].sortable;
5325     },
5326
5327     /**
5328      * Returns the rendering (formatting) function defined for the column.
5329      * @param {Number} col The column index.
5330      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5331      */
5332     getRenderer : function(col){
5333         if(!this.config[col].renderer){
5334             return Roo.grid.ColumnModel.defaultRenderer;
5335         }
5336         return this.config[col].renderer;
5337     },
5338
5339     /**
5340      * Sets the rendering (formatting) function for a column.
5341      * @param {Number} col The column index
5342      * @param {Function} fn The function to use to process the cell's raw data
5343      * to return HTML markup for the grid view. The render function is called with
5344      * the following parameters:<ul>
5345      * <li>Data value.</li>
5346      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347      * <li>css A CSS style string to apply to the table cell.</li>
5348      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350      * <li>Row index</li>
5351      * <li>Column index</li>
5352      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5353      */
5354     setRenderer : function(col, fn){
5355         this.config[col].renderer = fn;
5356     },
5357
5358     /**
5359      * Returns the width for the specified column.
5360      * @param {Number} col The column index
5361      * @return {Number}
5362      */
5363     getColumnWidth : function(col){
5364         return this.config[col].width * 1 || this.defaultWidth;
5365     },
5366
5367     /**
5368      * Sets the width for a column.
5369      * @param {Number} col The column index
5370      * @param {Number} width The new width
5371      */
5372     setColumnWidth : function(col, width, suppressEvent){
5373         this.config[col].width = width;
5374         this.totalWidth = null;
5375         if(!suppressEvent){
5376              this.fireEvent("widthchange", this, col, width);
5377         }
5378     },
5379
5380     /**
5381      * Returns the total width of all columns.
5382      * @param {Boolean} includeHidden True to include hidden column widths
5383      * @return {Number}
5384      */
5385     getTotalWidth : function(includeHidden){
5386         if(!this.totalWidth){
5387             this.totalWidth = 0;
5388             for(var i = 0, len = this.config.length; i < len; i++){
5389                 if(includeHidden || !this.isHidden(i)){
5390                     this.totalWidth += this.getColumnWidth(i);
5391                 }
5392             }
5393         }
5394         return this.totalWidth;
5395     },
5396
5397     /**
5398      * Returns the header for the specified column.
5399      * @param {Number} col The column index
5400      * @return {String}
5401      */
5402     getColumnHeader : function(col){
5403         return this.config[col].header;
5404     },
5405
5406     /**
5407      * Sets the header for a column.
5408      * @param {Number} col The column index
5409      * @param {String} header The new header
5410      */
5411     setColumnHeader : function(col, header){
5412         this.config[col].header = header;
5413         this.fireEvent("headerchange", this, col, header);
5414     },
5415
5416     /**
5417      * Returns the tooltip for the specified column.
5418      * @param {Number} col The column index
5419      * @return {String}
5420      */
5421     getColumnTooltip : function(col){
5422             return this.config[col].tooltip;
5423     },
5424     /**
5425      * Sets the tooltip for a column.
5426      * @param {Number} col The column index
5427      * @param {String} tooltip The new tooltip
5428      */
5429     setColumnTooltip : function(col, tooltip){
5430             this.config[col].tooltip = tooltip;
5431     },
5432
5433     /**
5434      * Returns the dataIndex for the specified column.
5435      * @param {Number} col The column index
5436      * @return {Number}
5437      */
5438     getDataIndex : function(col){
5439         return this.config[col].dataIndex;
5440     },
5441
5442     /**
5443      * Sets the dataIndex for a column.
5444      * @param {Number} col The column index
5445      * @param {Number} dataIndex The new dataIndex
5446      */
5447     setDataIndex : function(col, dataIndex){
5448         this.config[col].dataIndex = dataIndex;
5449     },
5450
5451     
5452     
5453     /**
5454      * Returns true if the cell is editable.
5455      * @param {Number} colIndex The column index
5456      * @param {Number} rowIndex The row index - this is nto actually used..?
5457      * @return {Boolean}
5458      */
5459     isCellEditable : function(colIndex, rowIndex){
5460         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5461     },
5462
5463     /**
5464      * Returns the editor defined for the cell/column.
5465      * return false or null to disable editing.
5466      * @param {Number} colIndex The column index
5467      * @param {Number} rowIndex The row index
5468      * @return {Object}
5469      */
5470     getCellEditor : function(colIndex, rowIndex){
5471         return this.config[colIndex].editor;
5472     },
5473
5474     /**
5475      * Sets if a column is editable.
5476      * @param {Number} col The column index
5477      * @param {Boolean} editable True if the column is editable
5478      */
5479     setEditable : function(col, editable){
5480         this.config[col].editable = editable;
5481     },
5482
5483
5484     /**
5485      * Returns true if the column is hidden.
5486      * @param {Number} colIndex The column index
5487      * @return {Boolean}
5488      */
5489     isHidden : function(colIndex){
5490         return this.config[colIndex].hidden;
5491     },
5492
5493
5494     /**
5495      * Returns true if the column width cannot be changed
5496      */
5497     isFixed : function(colIndex){
5498         return this.config[colIndex].fixed;
5499     },
5500
5501     /**
5502      * Returns true if the column can be resized
5503      * @return {Boolean}
5504      */
5505     isResizable : function(colIndex){
5506         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5507     },
5508     /**
5509      * Sets if a column is hidden.
5510      * @param {Number} colIndex The column index
5511      * @param {Boolean} hidden True if the column is hidden
5512      */
5513     setHidden : function(colIndex, hidden){
5514         this.config[colIndex].hidden = hidden;
5515         this.totalWidth = null;
5516         this.fireEvent("hiddenchange", this, colIndex, hidden);
5517     },
5518
5519     /**
5520      * Sets the editor for a column.
5521      * @param {Number} col The column index
5522      * @param {Object} editor The editor object
5523      */
5524     setEditor : function(col, editor){
5525         this.config[col].editor = editor;
5526     }
5527 });
5528
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530         if(typeof value == "string" && value.length < 1){
5531             return "&#160;";
5532         }
5533         return value;
5534 };
5535
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5538 /*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 /**
5550  * @class Roo.LoadMask
5551  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5552  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5554  * element's UpdateManager load indicator and will be destroyed after the initial load.
5555  * @constructor
5556  * Create a new LoadMask
5557  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558  * @param {Object} config The config object
5559  */
5560 Roo.LoadMask = function(el, config){
5561     this.el = Roo.get(el);
5562     Roo.apply(this, config);
5563     if(this.store){
5564         this.store.on('beforeload', this.onBeforeLoad, this);
5565         this.store.on('load', this.onLoad, this);
5566         this.store.on('loadexception', this.onLoadException, this);
5567         this.removeMask = false;
5568     }else{
5569         var um = this.el.getUpdateManager();
5570         um.showLoadIndicator = false; // disable the default indicator
5571         um.on('beforeupdate', this.onBeforeLoad, this);
5572         um.on('update', this.onLoad, this);
5573         um.on('failure', this.onLoad, this);
5574         this.removeMask = true;
5575     }
5576 };
5577
5578 Roo.LoadMask.prototype = {
5579     /**
5580      * @cfg {Boolean} removeMask
5581      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5583      */
5584     /**
5585      * @cfg {String} msg
5586      * The text to display in a centered loading message box (defaults to 'Loading...')
5587      */
5588     msg : 'Loading...',
5589     /**
5590      * @cfg {String} msgCls
5591      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5592      */
5593     msgCls : 'x-mask-loading',
5594
5595     /**
5596      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5597      * @type Boolean
5598      */
5599     disabled: false,
5600
5601     /**
5602      * Disables the mask to prevent it from being displayed
5603      */
5604     disable : function(){
5605        this.disabled = true;
5606     },
5607
5608     /**
5609      * Enables the mask so that it can be displayed
5610      */
5611     enable : function(){
5612         this.disabled = false;
5613     },
5614     
5615     onLoadException : function()
5616     {
5617         Roo.log(arguments);
5618         
5619         if (typeof(arguments[3]) != 'undefined') {
5620             Roo.MessageBox.alert("Error loading",arguments[3]);
5621         } 
5622         /*
5623         try {
5624             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5626             }   
5627         } catch(e) {
5628             
5629         }
5630         */
5631     
5632         
5633         
5634         this.el.unmask(this.removeMask);
5635     },
5636     // private
5637     onLoad : function()
5638     {
5639         this.el.unmask(this.removeMask);
5640     },
5641
5642     // private
5643     onBeforeLoad : function(){
5644         if(!this.disabled){
5645             this.el.mask(this.msg, this.msgCls);
5646         }
5647     },
5648
5649     // private
5650     destroy : function(){
5651         if(this.store){
5652             this.store.un('beforeload', this.onBeforeLoad, this);
5653             this.store.un('load', this.onLoad, this);
5654             this.store.un('loadexception', this.onLoadException, this);
5655         }else{
5656             var um = this.el.getUpdateManager();
5657             um.un('beforeupdate', this.onBeforeLoad, this);
5658             um.un('update', this.onLoad, this);
5659             um.un('failure', this.onLoad, this);
5660         }
5661     }
5662 };/*
5663  * - LGPL
5664  *
5665  * table
5666  * 
5667  */
5668
5669 /**
5670  * @class Roo.bootstrap.Table
5671  * @extends Roo.bootstrap.Component
5672  * Bootstrap Table class
5673  * @cfg {String} cls table class
5674  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675  * @cfg {String} bgcolor Specifies the background color for a table
5676  * @cfg {Number} border Specifies whether the table cells should have borders or not
5677  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678  * @cfg {Number} cellspacing Specifies the space between cells
5679  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681  * @cfg {String} sortable Specifies that the table should be sortable
5682  * @cfg {String} summary Specifies a summary of the content of a table
5683  * @cfg {Number} width Specifies the width of a table
5684  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5685  * 
5686  * @cfg {boolean} striped Should the rows be alternative striped
5687  * @cfg {boolean} bordered Add borders to the table
5688  * @cfg {boolean} hover Add hover highlighting
5689  * @cfg {boolean} condensed Format condensed
5690  * @cfg {boolean} responsive Format condensed
5691  * @cfg {Boolean} loadMask (true|false) default false
5692  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694  * @cfg {Boolean} rowSelection (true|false) default false
5695  * @cfg {Boolean} cellSelection (true|false) default false
5696  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5698  
5699  * 
5700  * @constructor
5701  * Create a new Table
5702  * @param {Object} config The config object
5703  */
5704
5705 Roo.bootstrap.Table = function(config){
5706     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5707     
5708   
5709     
5710     // BC...
5711     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5715     
5716     
5717     if (this.sm) {
5718         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5719         this.sm = this.selModel;
5720         this.sm.xmodule = this.xmodule || false;
5721     }
5722     if (this.cm && typeof(this.cm.config) == 'undefined') {
5723         this.colModel = new Roo.grid.ColumnModel(this.cm);
5724         this.cm = this.colModel;
5725         this.cm.xmodule = this.xmodule || false;
5726     }
5727     if (this.store) {
5728         this.store= Roo.factory(this.store, Roo.data);
5729         this.ds = this.store;
5730         this.ds.xmodule = this.xmodule || false;
5731          
5732     }
5733     if (this.footer && this.store) {
5734         this.footer.dataSource = this.ds;
5735         this.footer = Roo.factory(this.footer);
5736     }
5737     
5738     /** @private */
5739     this.addEvents({
5740         /**
5741          * @event cellclick
5742          * Fires when a cell is clicked
5743          * @param {Roo.bootstrap.Table} this
5744          * @param {Roo.Element} el
5745          * @param {Number} rowIndex
5746          * @param {Number} columnIndex
5747          * @param {Roo.EventObject} e
5748          */
5749         "cellclick" : true,
5750         /**
5751          * @event celldblclick
5752          * Fires when a cell is double clicked
5753          * @param {Roo.bootstrap.Table} this
5754          * @param {Roo.Element} el
5755          * @param {Number} rowIndex
5756          * @param {Number} columnIndex
5757          * @param {Roo.EventObject} e
5758          */
5759         "celldblclick" : true,
5760         /**
5761          * @event rowclick
5762          * Fires when a row is clicked
5763          * @param {Roo.bootstrap.Table} this
5764          * @param {Roo.Element} el
5765          * @param {Number} rowIndex
5766          * @param {Roo.EventObject} e
5767          */
5768         "rowclick" : true,
5769         /**
5770          * @event rowdblclick
5771          * Fires when a row is double clicked
5772          * @param {Roo.bootstrap.Table} this
5773          * @param {Roo.Element} el
5774          * @param {Number} rowIndex
5775          * @param {Roo.EventObject} e
5776          */
5777         "rowdblclick" : true,
5778         /**
5779          * @event mouseover
5780          * Fires when a mouseover occur
5781          * @param {Roo.bootstrap.Table} this
5782          * @param {Roo.Element} el
5783          * @param {Number} rowIndex
5784          * @param {Number} columnIndex
5785          * @param {Roo.EventObject} e
5786          */
5787         "mouseover" : true,
5788         /**
5789          * @event mouseout
5790          * Fires when a mouseout occur
5791          * @param {Roo.bootstrap.Table} this
5792          * @param {Roo.Element} el
5793          * @param {Number} rowIndex
5794          * @param {Number} columnIndex
5795          * @param {Roo.EventObject} e
5796          */
5797         "mouseout" : true,
5798         /**
5799          * @event rowclass
5800          * Fires when a row is rendered, so you can change add a style to it.
5801          * @param {Roo.bootstrap.Table} this
5802          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5803          */
5804         'rowclass' : true,
5805           /**
5806          * @event rowsrendered
5807          * Fires when all the  rows have been rendered
5808          * @param {Roo.bootstrap.Table} this
5809          */
5810         'rowsrendered' : true,
5811         /**
5812          * @event contextmenu
5813          * The raw contextmenu event for the entire grid.
5814          * @param {Roo.EventObject} e
5815          */
5816         "contextmenu" : true,
5817         /**
5818          * @event rowcontextmenu
5819          * Fires when a row is right clicked
5820          * @param {Roo.bootstrap.Table} this
5821          * @param {Number} rowIndex
5822          * @param {Roo.EventObject} e
5823          */
5824         "rowcontextmenu" : true,
5825         /**
5826          * @event cellcontextmenu
5827          * Fires when a cell is right clicked
5828          * @param {Roo.bootstrap.Table} this
5829          * @param {Number} rowIndex
5830          * @param {Number} cellIndex
5831          * @param {Roo.EventObject} e
5832          */
5833          "cellcontextmenu" : true,
5834          /**
5835          * @event headercontextmenu
5836          * Fires when a header is right clicked
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Number} columnIndex
5839          * @param {Roo.EventObject} e
5840          */
5841         "headercontextmenu" : true
5842     });
5843 };
5844
5845 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5846     
5847     cls: false,
5848     align: false,
5849     bgcolor: false,
5850     border: false,
5851     cellpadding: false,
5852     cellspacing: false,
5853     frame: false,
5854     rules: false,
5855     sortable: false,
5856     summary: false,
5857     width: false,
5858     striped : false,
5859     scrollBody : false,
5860     bordered: false,
5861     hover:  false,
5862     condensed : false,
5863     responsive : false,
5864     sm : false,
5865     cm : false,
5866     store : false,
5867     loadMask : false,
5868     footerShow : true,
5869     headerShow : true,
5870   
5871     rowSelection : false,
5872     cellSelection : false,
5873     layout : false,
5874     
5875     // Roo.Element - the tbody
5876     mainBody: false,
5877     // Roo.Element - thead element
5878     mainHead: false,
5879     
5880     container: false, // used by gridpanel...
5881     
5882     getAutoCreate : function()
5883     {
5884         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5885         
5886         cfg = {
5887             tag: 'table',
5888             cls : 'table',
5889             cn : []
5890         };
5891         if (this.scrollBody) {
5892             cfg.cls += ' table-body-fixed';
5893         }    
5894         if (this.striped) {
5895             cfg.cls += ' table-striped';
5896         }
5897         
5898         if (this.hover) {
5899             cfg.cls += ' table-hover';
5900         }
5901         if (this.bordered) {
5902             cfg.cls += ' table-bordered';
5903         }
5904         if (this.condensed) {
5905             cfg.cls += ' table-condensed';
5906         }
5907         if (this.responsive) {
5908             cfg.cls += ' table-responsive';
5909         }
5910         
5911         if (this.cls) {
5912             cfg.cls+=  ' ' +this.cls;
5913         }
5914         
5915         // this lot should be simplifed...
5916         
5917         if (this.align) {
5918             cfg.align=this.align;
5919         }
5920         if (this.bgcolor) {
5921             cfg.bgcolor=this.bgcolor;
5922         }
5923         if (this.border) {
5924             cfg.border=this.border;
5925         }
5926         if (this.cellpadding) {
5927             cfg.cellpadding=this.cellpadding;
5928         }
5929         if (this.cellspacing) {
5930             cfg.cellspacing=this.cellspacing;
5931         }
5932         if (this.frame) {
5933             cfg.frame=this.frame;
5934         }
5935         if (this.rules) {
5936             cfg.rules=this.rules;
5937         }
5938         if (this.sortable) {
5939             cfg.sortable=this.sortable;
5940         }
5941         if (this.summary) {
5942             cfg.summary=this.summary;
5943         }
5944         if (this.width) {
5945             cfg.width=this.width;
5946         }
5947         if (this.layout) {
5948             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5949         }
5950         
5951         if(this.store || this.cm){
5952             if(this.headerShow){
5953                 cfg.cn.push(this.renderHeader());
5954             }
5955             
5956             cfg.cn.push(this.renderBody());
5957             
5958             if(this.footerShow){
5959                 cfg.cn.push(this.renderFooter());
5960             }
5961             // where does this come from?
5962             //cfg.cls+=  ' TableGrid';
5963         }
5964         
5965         return { cn : [ cfg ] };
5966     },
5967     
5968     initEvents : function()
5969     {   
5970         if(!this.store || !this.cm){
5971             return;
5972         }
5973         
5974         //Roo.log('initEvents with ds!!!!');
5975         
5976         this.mainBody = this.el.select('tbody', true).first();
5977         this.mainHead = this.el.select('thead', true).first();
5978         
5979         
5980         
5981         var _this = this;
5982         
5983         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5984             e.on('click', _this.sort, _this);
5985         });
5986         
5987         this.el.on("click", this.onClick, this);
5988         this.el.on("dblclick", this.onDblClick, this);
5989         
5990         // why is this done????? = it breaks dialogs??
5991         //this.parent().el.setStyle('position', 'relative');
5992         
5993         
5994         if (this.footer) {
5995             this.footer.parentId = this.id;
5996             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5997         }
5998         
5999         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6000         
6001         this.store.on('load', this.onLoad, this);
6002         this.store.on('beforeload', this.onBeforeLoad, this);
6003         this.store.on('update', this.onUpdate, this);
6004         this.store.on('add', this.onAdd, this);
6005         this.store.on("clear", this.clear, this);
6006         
6007         this.el.on("contextmenu", this.onContextMenu, this);
6008         
6009         this.mainBody.on('scroll', this.onBodyScroll, this);
6010         
6011         
6012     },
6013     
6014     onContextMenu : function(e, t)
6015     {
6016         this.processEvent("contextmenu", e);
6017     },
6018     
6019     processEvent : function(name, e)
6020     {
6021         if (name != 'touchstart' ) {
6022             this.fireEvent(name, e);    
6023         }
6024         
6025         var t = e.getTarget();
6026         
6027         var cell = Roo.get(t);
6028         
6029         if(!cell){
6030             return;
6031         }
6032         
6033         if(cell.findParent('tfoot', false, true)){
6034             return;
6035         }
6036         
6037         if(cell.findParent('thead', false, true)){
6038             
6039             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6040                 cell = Roo.get(t).findParent('th', false, true);
6041                 if (!cell) {
6042                     Roo.log("failed to find th in thead?");
6043                     Roo.log(e.getTarget());
6044                     return;
6045                 }
6046             }
6047             
6048             var cellIndex = cell.dom.cellIndex;
6049             
6050             var ename = name == 'touchstart' ? 'click' : name;
6051             this.fireEvent("header" + ename, this, cellIndex, e);
6052             
6053             return;
6054         }
6055         
6056         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6057             cell = Roo.get(t).findParent('td', false, true);
6058             if (!cell) {
6059                 Roo.log("failed to find th in tbody?");
6060                 Roo.log(e.getTarget());
6061                 return;
6062             }
6063         }
6064         
6065         var row = cell.findParent('tr', false, true);
6066         var cellIndex = cell.dom.cellIndex;
6067         var rowIndex = row.dom.rowIndex - 1;
6068         
6069         if(row !== false){
6070             
6071             this.fireEvent("row" + name, this, rowIndex, e);
6072             
6073             if(cell !== false){
6074             
6075                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6076             }
6077         }
6078         
6079     },
6080     
6081     onMouseover : function(e, el)
6082     {
6083         var cell = Roo.get(el);
6084         
6085         if(!cell){
6086             return;
6087         }
6088         
6089         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6090             cell = cell.findParent('td', false, true);
6091         }
6092         
6093         var row = cell.findParent('tr', false, true);
6094         var cellIndex = cell.dom.cellIndex;
6095         var rowIndex = row.dom.rowIndex - 1; // start from 0
6096         
6097         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6098         
6099     },
6100     
6101     onMouseout : function(e, el)
6102     {
6103         var cell = Roo.get(el);
6104         
6105         if(!cell){
6106             return;
6107         }
6108         
6109         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110             cell = cell.findParent('td', false, true);
6111         }
6112         
6113         var row = cell.findParent('tr', false, true);
6114         var cellIndex = cell.dom.cellIndex;
6115         var rowIndex = row.dom.rowIndex - 1; // start from 0
6116         
6117         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6118         
6119     },
6120     
6121     onClick : function(e, el)
6122     {
6123         var cell = Roo.get(el);
6124         
6125         if(!cell || (!this.cellSelection && !this.rowSelection)){
6126             return;
6127         }
6128         
6129         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130             cell = cell.findParent('td', false, true);
6131         }
6132         
6133         if(!cell || typeof(cell) == 'undefined'){
6134             return;
6135         }
6136         
6137         var row = cell.findParent('tr', false, true);
6138         
6139         if(!row || typeof(row) == 'undefined'){
6140             return;
6141         }
6142         
6143         var cellIndex = cell.dom.cellIndex;
6144         var rowIndex = this.getRowIndex(row);
6145         
6146         // why??? - should these not be based on SelectionModel?
6147         if(this.cellSelection){
6148             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6149         }
6150         
6151         if(this.rowSelection){
6152             this.fireEvent('rowclick', this, row, rowIndex, e);
6153         }
6154         
6155         
6156     },
6157     
6158     onDblClick : function(e,el)
6159     {
6160         var cell = Roo.get(el);
6161         
6162         if(!cell || (!this.CellSelection && !this.RowSelection)){
6163             return;
6164         }
6165         
6166         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6167             cell = cell.findParent('td', false, true);
6168         }
6169         
6170         if(!cell || typeof(cell) == 'undefined'){
6171             return;
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         
6176         if(!row || typeof(row) == 'undefined'){
6177             return;
6178         }
6179         
6180         var cellIndex = cell.dom.cellIndex;
6181         var rowIndex = this.getRowIndex(row);
6182         
6183         if(this.CellSelection){
6184             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6185         }
6186         
6187         if(this.RowSelection){
6188             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6189         }
6190     },
6191     
6192     sort : function(e,el)
6193     {
6194         var col = Roo.get(el);
6195         
6196         if(!col.hasClass('sortable')){
6197             return;
6198         }
6199         
6200         var sort = col.attr('sort');
6201         var dir = 'ASC';
6202         
6203         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6204             dir = 'DESC';
6205         }
6206         
6207         this.store.sortInfo = {field : sort, direction : dir};
6208         
6209         if (this.footer) {
6210             Roo.log("calling footer first");
6211             this.footer.onClick('first');
6212         } else {
6213         
6214             this.store.load({ params : { start : 0 } });
6215         }
6216     },
6217     
6218     renderHeader : function()
6219     {
6220         var header = {
6221             tag: 'thead',
6222             cn : []
6223         };
6224         
6225         var cm = this.cm;
6226         this.totalWidth = 0;
6227         
6228         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6229             
6230             var config = cm.config[i];
6231             
6232             var c = {
6233                 tag: 'th',
6234                 style : '',
6235                 html: cm.getColumnHeader(i)
6236             };
6237             
6238             var hh = '';
6239             
6240             if(typeof(config.sortable) != 'undefined' && config.sortable){
6241                 c.cls = 'sortable';
6242                 c.html = '<i class="glyphicon"></i>' + c.html;
6243             }
6244             
6245             if(typeof(config.lgHeader) != 'undefined'){
6246                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6247             }
6248             
6249             if(typeof(config.mdHeader) != 'undefined'){
6250                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6251             }
6252             
6253             if(typeof(config.smHeader) != 'undefined'){
6254                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6255             }
6256             
6257             if(typeof(config.xsHeader) != 'undefined'){
6258                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6259             }
6260             
6261             if(hh.length){
6262                 c.html = hh;
6263             }
6264             
6265             if(typeof(config.tooltip) != 'undefined'){
6266                 c.tooltip = config.tooltip;
6267             }
6268             
6269             if(typeof(config.colspan) != 'undefined'){
6270                 c.colspan = config.colspan;
6271             }
6272             
6273             if(typeof(config.hidden) != 'undefined' && config.hidden){
6274                 c.style += ' display:none;';
6275             }
6276             
6277             if(typeof(config.dataIndex) != 'undefined'){
6278                 c.sort = config.dataIndex;
6279             }
6280             
6281            
6282             
6283             if(typeof(config.align) != 'undefined' && config.align.length){
6284                 c.style += ' text-align:' + config.align + ';';
6285             }
6286             
6287             if(typeof(config.width) != 'undefined'){
6288                 c.style += ' width:' + config.width + 'px;';
6289                 this.totalWidth += config.width;
6290             } else {
6291                 this.totalWidth += 100; // assume minimum of 100 per column?
6292             }
6293             
6294             if(typeof(config.cls) != 'undefined'){
6295                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6296             }
6297             
6298             ['xs','sm','md','lg'].map(function(size){
6299                 
6300                 if(typeof(config[size]) == 'undefined'){
6301                     return;
6302                 }
6303                 
6304                 if (!config[size]) { // 0 = hidden
6305                     c.cls += ' hidden-' + size;
6306                     return;
6307                 }
6308                 
6309                 c.cls += ' col-' + size + '-' + config[size];
6310
6311             });
6312             
6313             header.cn.push(c)
6314         }
6315         
6316         return header;
6317     },
6318     
6319     renderBody : function()
6320     {
6321         var body = {
6322             tag: 'tbody',
6323             cn : [
6324                 {
6325                     tag: 'tr',
6326                     cn : [
6327                         {
6328                             tag : 'td',
6329                             colspan :  this.cm.getColumnCount()
6330                         }
6331                     ]
6332                 }
6333             ]
6334         };
6335         
6336         return body;
6337     },
6338     
6339     renderFooter : function()
6340     {
6341         var footer = {
6342             tag: 'tfoot',
6343             cn : [
6344                 {
6345                     tag: 'tr',
6346                     cn : [
6347                         {
6348                             tag : 'td',
6349                             colspan :  this.cm.getColumnCount()
6350                         }
6351                     ]
6352                 }
6353             ]
6354         };
6355         
6356         return footer;
6357     },
6358     
6359     
6360     
6361     onLoad : function()
6362     {
6363 //        Roo.log('ds onload');
6364         this.clear();
6365         
6366         var _this = this;
6367         var cm = this.cm;
6368         var ds = this.store;
6369         
6370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6372             if (_this.store.sortInfo) {
6373                     
6374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6376                 }
6377                 
6378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6380                 }
6381             }
6382         });
6383         
6384         var tbody =  this.mainBody;
6385               
6386         if(ds.getCount() > 0){
6387             ds.data.each(function(d,rowIndex){
6388                 var row =  this.renderRow(cm, ds, rowIndex);
6389                 
6390                 tbody.createChild(row);
6391                 
6392                 var _this = this;
6393                 
6394                 if(row.cellObjects.length){
6395                     Roo.each(row.cellObjects, function(r){
6396                         _this.renderCellObject(r);
6397                     })
6398                 }
6399                 
6400             }, this);
6401         }
6402         
6403         Roo.each(this.el.select('tbody td', true).elements, function(e){
6404             e.on('mouseover', _this.onMouseover, _this);
6405         });
6406         
6407         Roo.each(this.el.select('tbody td', true).elements, function(e){
6408             e.on('mouseout', _this.onMouseout, _this);
6409         });
6410         this.fireEvent('rowsrendered', this);
6411         //if(this.loadMask){
6412         //    this.maskEl.hide();
6413         //}
6414         
6415         this.autoSize();
6416     },
6417     
6418     
6419     onUpdate : function(ds,record)
6420     {
6421         this.refreshRow(record);
6422     },
6423     
6424     onRemove : function(ds, record, index, isUpdate){
6425         if(isUpdate !== true){
6426             this.fireEvent("beforerowremoved", this, index, record);
6427         }
6428         var bt = this.mainBody.dom;
6429         
6430         var rows = this.el.select('tbody > tr', true).elements;
6431         
6432         if(typeof(rows[index]) != 'undefined'){
6433             bt.removeChild(rows[index].dom);
6434         }
6435         
6436 //        if(bt.rows[index]){
6437 //            bt.removeChild(bt.rows[index]);
6438 //        }
6439         
6440         if(isUpdate !== true){
6441             //this.stripeRows(index);
6442             //this.syncRowHeights(index, index);
6443             //this.layout();
6444             this.fireEvent("rowremoved", this, index, record);
6445         }
6446     },
6447     
6448     onAdd : function(ds, records, rowIndex)
6449     {
6450         //Roo.log('on Add called');
6451         // - note this does not handle multiple adding very well..
6452         var bt = this.mainBody.dom;
6453         for (var i =0 ; i < records.length;i++) {
6454             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6455             //Roo.log(records[i]);
6456             //Roo.log(this.store.getAt(rowIndex+i));
6457             this.insertRow(this.store, rowIndex + i, false);
6458             return;
6459         }
6460         
6461     },
6462     
6463     
6464     refreshRow : function(record){
6465         var ds = this.store, index;
6466         if(typeof record == 'number'){
6467             index = record;
6468             record = ds.getAt(index);
6469         }else{
6470             index = ds.indexOf(record);
6471         }
6472         this.insertRow(ds, index, true);
6473         this.onRemove(ds, record, index+1, true);
6474         //this.syncRowHeights(index, index);
6475         //this.layout();
6476         this.fireEvent("rowupdated", this, index, record);
6477     },
6478     
6479     insertRow : function(dm, rowIndex, isUpdate){
6480         
6481         if(!isUpdate){
6482             this.fireEvent("beforerowsinserted", this, rowIndex);
6483         }
6484             //var s = this.getScrollState();
6485         var row = this.renderRow(this.cm, this.store, rowIndex);
6486         // insert before rowIndex..
6487         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6488         
6489         var _this = this;
6490                 
6491         if(row.cellObjects.length){
6492             Roo.each(row.cellObjects, function(r){
6493                 _this.renderCellObject(r);
6494             })
6495         }
6496             
6497         if(!isUpdate){
6498             this.fireEvent("rowsinserted", this, rowIndex);
6499             //this.syncRowHeights(firstRow, lastRow);
6500             //this.stripeRows(firstRow);
6501             //this.layout();
6502         }
6503         
6504     },
6505     
6506     
6507     getRowDom : function(rowIndex)
6508     {
6509         var rows = this.el.select('tbody > tr', true).elements;
6510         
6511         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6512         
6513     },
6514     // returns the object tree for a tr..
6515   
6516     
6517     renderRow : function(cm, ds, rowIndex) 
6518     {
6519         
6520         var d = ds.getAt(rowIndex);
6521         
6522         var row = {
6523             tag : 'tr',
6524             cn : []
6525         };
6526             
6527         var cellObjects = [];
6528         
6529         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6530             var config = cm.config[i];
6531             
6532             var renderer = cm.getRenderer(i);
6533             var value = '';
6534             var id = false;
6535             
6536             if(typeof(renderer) !== 'undefined'){
6537                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6538             }
6539             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6540             // and are rendered into the cells after the row is rendered - using the id for the element.
6541             
6542             if(typeof(value) === 'object'){
6543                 id = Roo.id();
6544                 cellObjects.push({
6545                     container : id,
6546                     cfg : value 
6547                 })
6548             }
6549             
6550             var rowcfg = {
6551                 record: d,
6552                 rowIndex : rowIndex,
6553                 colIndex : i,
6554                 rowClass : ''
6555             };
6556
6557             this.fireEvent('rowclass', this, rowcfg);
6558             
6559             var td = {
6560                 tag: 'td',
6561                 cls : rowcfg.rowClass,
6562                 style: '',
6563                 html: (typeof(value) === 'object') ? '' : value
6564             };
6565             
6566             if (id) {
6567                 td.id = id;
6568             }
6569             
6570             if(typeof(config.colspan) != 'undefined'){
6571                 td.colspan = config.colspan;
6572             }
6573             
6574             if(typeof(config.hidden) != 'undefined' && config.hidden){
6575                 td.style += ' display:none;';
6576             }
6577             
6578             if(typeof(config.align) != 'undefined' && config.align.length){
6579                 td.style += ' text-align:' + config.align + ';';
6580             }
6581             
6582             if(typeof(config.width) != 'undefined'){
6583                 td.style += ' width:' +  config.width + 'px;';
6584             }
6585             
6586             if(typeof(config.cursor) != 'undefined'){
6587                 td.style += ' cursor:' +  config.cursor + ';';
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     td.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 td.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608              
6609             row.cn.push(td);
6610            
6611         }
6612         
6613         row.cellObjects = cellObjects;
6614         
6615         return row;
6616           
6617     },
6618     
6619     
6620     
6621     onBeforeLoad : function()
6622     {
6623         //Roo.log('ds onBeforeLoad');
6624         
6625         //this.clear();
6626         
6627         //if(this.loadMask){
6628         //    this.maskEl.show();
6629         //}
6630     },
6631      /**
6632      * Remove all rows
6633      */
6634     clear : function()
6635     {
6636         this.el.select('tbody', true).first().dom.innerHTML = '';
6637     },
6638     /**
6639      * Show or hide a row.
6640      * @param {Number} rowIndex to show or hide
6641      * @param {Boolean} state hide
6642      */
6643     setRowVisibility : function(rowIndex, state)
6644     {
6645         var bt = this.mainBody.dom;
6646         
6647         var rows = this.el.select('tbody > tr', true).elements;
6648         
6649         if(typeof(rows[rowIndex]) == 'undefined'){
6650             return;
6651         }
6652         rows[rowIndex].dom.style.display = state ? '' : 'none';
6653     },
6654     
6655     
6656     getSelectionModel : function(){
6657         if(!this.selModel){
6658             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6659         }
6660         return this.selModel;
6661     },
6662     /*
6663      * Render the Roo.bootstrap object from renderder
6664      */
6665     renderCellObject : function(r)
6666     {
6667         var _this = this;
6668         
6669         var t = r.cfg.render(r.container);
6670         
6671         if(r.cfg.cn){
6672             Roo.each(r.cfg.cn, function(c){
6673                 var child = {
6674                     container: t.getChildContainer(),
6675                     cfg: c
6676                 };
6677                 _this.renderCellObject(child);
6678             })
6679         }
6680     },
6681     
6682     getRowIndex : function(row)
6683     {
6684         var rowIndex = -1;
6685         
6686         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6687             if(el != row){
6688                 return;
6689             }
6690             
6691             rowIndex = index;
6692         });
6693         
6694         return rowIndex;
6695     },
6696      /**
6697      * Returns the grid's underlying element = used by panel.Grid
6698      * @return {Element} The element
6699      */
6700     getGridEl : function(){
6701         return this.el;
6702     },
6703      /**
6704      * Forces a resize - used by panel.Grid
6705      * @return {Element} The element
6706      */
6707     autoSize : function()
6708     {
6709         //var ctr = Roo.get(this.container.dom.parentElement);
6710         var ctr = Roo.get(this.el.dom);
6711         
6712         var thd = this.getGridEl().select('thead',true).first();
6713         var tbd = this.getGridEl().select('tbody', true).first();
6714         
6715         
6716         var cw = ctr.getWidth();
6717         
6718         if (tbd) {
6719             
6720             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6721             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6722             cw -= barsize;
6723         }
6724         cw = Math.max(cw, this.totalWidth);
6725         this.getGridEl().select('tr',true).setWidth(cw);
6726         // resize 'expandable coloumn?
6727         
6728         return; // we doe not have a view in this design..
6729         
6730     },
6731     onBodyScroll: function()
6732     {
6733         
6734         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6735         this.mainHead.setStyle({
6736                     'position' : 'relative',
6737                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6738         });
6739         
6740         
6741     }
6742 });
6743
6744  
6745
6746  /*
6747  * - LGPL
6748  *
6749  * table cell
6750  * 
6751  */
6752
6753 /**
6754  * @class Roo.bootstrap.TableCell
6755  * @extends Roo.bootstrap.Component
6756  * Bootstrap TableCell class
6757  * @cfg {String} html cell contain text
6758  * @cfg {String} cls cell class
6759  * @cfg {String} tag cell tag (td|th) default td
6760  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6761  * @cfg {String} align Aligns the content in a cell
6762  * @cfg {String} axis Categorizes cells
6763  * @cfg {String} bgcolor Specifies the background color of a cell
6764  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6765  * @cfg {Number} colspan Specifies the number of columns a cell should span
6766  * @cfg {String} headers Specifies one or more header cells a cell is related to
6767  * @cfg {Number} height Sets the height of a cell
6768  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6769  * @cfg {Number} rowspan Sets the number of rows a cell should span
6770  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6771  * @cfg {String} valign Vertical aligns the content in a cell
6772  * @cfg {Number} width Specifies the width of a cell
6773  * 
6774  * @constructor
6775  * Create a new TableCell
6776  * @param {Object} config The config object
6777  */
6778
6779 Roo.bootstrap.TableCell = function(config){
6780     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6781 };
6782
6783 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6784     
6785     html: false,
6786     cls: false,
6787     tag: false,
6788     abbr: false,
6789     align: false,
6790     axis: false,
6791     bgcolor: false,
6792     charoff: false,
6793     colspan: false,
6794     headers: false,
6795     height: false,
6796     nowrap: false,
6797     rowspan: false,
6798     scope: false,
6799     valign: false,
6800     width: false,
6801     
6802     
6803     getAutoCreate : function(){
6804         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6805         
6806         cfg = {
6807             tag: 'td'
6808         };
6809         
6810         if(this.tag){
6811             cfg.tag = this.tag;
6812         }
6813         
6814         if (this.html) {
6815             cfg.html=this.html
6816         }
6817         if (this.cls) {
6818             cfg.cls=this.cls
6819         }
6820         if (this.abbr) {
6821             cfg.abbr=this.abbr
6822         }
6823         if (this.align) {
6824             cfg.align=this.align
6825         }
6826         if (this.axis) {
6827             cfg.axis=this.axis
6828         }
6829         if (this.bgcolor) {
6830             cfg.bgcolor=this.bgcolor
6831         }
6832         if (this.charoff) {
6833             cfg.charoff=this.charoff
6834         }
6835         if (this.colspan) {
6836             cfg.colspan=this.colspan
6837         }
6838         if (this.headers) {
6839             cfg.headers=this.headers
6840         }
6841         if (this.height) {
6842             cfg.height=this.height
6843         }
6844         if (this.nowrap) {
6845             cfg.nowrap=this.nowrap
6846         }
6847         if (this.rowspan) {
6848             cfg.rowspan=this.rowspan
6849         }
6850         if (this.scope) {
6851             cfg.scope=this.scope
6852         }
6853         if (this.valign) {
6854             cfg.valign=this.valign
6855         }
6856         if (this.width) {
6857             cfg.width=this.width
6858         }
6859         
6860         
6861         return cfg;
6862     }
6863    
6864 });
6865
6866  
6867
6868  /*
6869  * - LGPL
6870  *
6871  * table row
6872  * 
6873  */
6874
6875 /**
6876  * @class Roo.bootstrap.TableRow
6877  * @extends Roo.bootstrap.Component
6878  * Bootstrap TableRow class
6879  * @cfg {String} cls row class
6880  * @cfg {String} align Aligns the content in a table row
6881  * @cfg {String} bgcolor Specifies a background color for a table row
6882  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6883  * @cfg {String} valign Vertical aligns the content in a table row
6884  * 
6885  * @constructor
6886  * Create a new TableRow
6887  * @param {Object} config The config object
6888  */
6889
6890 Roo.bootstrap.TableRow = function(config){
6891     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6892 };
6893
6894 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6895     
6896     cls: false,
6897     align: false,
6898     bgcolor: false,
6899     charoff: false,
6900     valign: false,
6901     
6902     getAutoCreate : function(){
6903         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6904         
6905         cfg = {
6906             tag: 'tr'
6907         };
6908             
6909         if(this.cls){
6910             cfg.cls = this.cls;
6911         }
6912         if(this.align){
6913             cfg.align = this.align;
6914         }
6915         if(this.bgcolor){
6916             cfg.bgcolor = this.bgcolor;
6917         }
6918         if(this.charoff){
6919             cfg.charoff = this.charoff;
6920         }
6921         if(this.valign){
6922             cfg.valign = this.valign;
6923         }
6924         
6925         return cfg;
6926     }
6927    
6928 });
6929
6930  
6931
6932  /*
6933  * - LGPL
6934  *
6935  * table body
6936  * 
6937  */
6938
6939 /**
6940  * @class Roo.bootstrap.TableBody
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap TableBody class
6943  * @cfg {String} cls element class
6944  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6945  * @cfg {String} align Aligns the content inside the element
6946  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6947  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6948  * 
6949  * @constructor
6950  * Create a new TableBody
6951  * @param {Object} config The config object
6952  */
6953
6954 Roo.bootstrap.TableBody = function(config){
6955     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6956 };
6957
6958 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6959     
6960     cls: false,
6961     tag: false,
6962     align: false,
6963     charoff: false,
6964     valign: false,
6965     
6966     getAutoCreate : function(){
6967         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6968         
6969         cfg = {
6970             tag: 'tbody'
6971         };
6972             
6973         if (this.cls) {
6974             cfg.cls=this.cls
6975         }
6976         if(this.tag){
6977             cfg.tag = this.tag;
6978         }
6979         
6980         if(this.align){
6981             cfg.align = this.align;
6982         }
6983         if(this.charoff){
6984             cfg.charoff = this.charoff;
6985         }
6986         if(this.valign){
6987             cfg.valign = this.valign;
6988         }
6989         
6990         return cfg;
6991     }
6992     
6993     
6994 //    initEvents : function()
6995 //    {
6996 //        
6997 //        if(!this.store){
6998 //            return;
6999 //        }
7000 //        
7001 //        this.store = Roo.factory(this.store, Roo.data);
7002 //        this.store.on('load', this.onLoad, this);
7003 //        
7004 //        this.store.load();
7005 //        
7006 //    },
7007 //    
7008 //    onLoad: function () 
7009 //    {   
7010 //        this.fireEvent('load', this);
7011 //    }
7012 //    
7013 //   
7014 });
7015
7016  
7017
7018  /*
7019  * Based on:
7020  * Ext JS Library 1.1.1
7021  * Copyright(c) 2006-2007, Ext JS, LLC.
7022  *
7023  * Originally Released Under LGPL - original licence link has changed is not relivant.
7024  *
7025  * Fork - LGPL
7026  * <script type="text/javascript">
7027  */
7028
7029 // as we use this in bootstrap.
7030 Roo.namespace('Roo.form');
7031  /**
7032  * @class Roo.form.Action
7033  * Internal Class used to handle form actions
7034  * @constructor
7035  * @param {Roo.form.BasicForm} el The form element or its id
7036  * @param {Object} config Configuration options
7037  */
7038
7039  
7040  
7041 // define the action interface
7042 Roo.form.Action = function(form, options){
7043     this.form = form;
7044     this.options = options || {};
7045 };
7046 /**
7047  * Client Validation Failed
7048  * @const 
7049  */
7050 Roo.form.Action.CLIENT_INVALID = 'client';
7051 /**
7052  * Server Validation Failed
7053  * @const 
7054  */
7055 Roo.form.Action.SERVER_INVALID = 'server';
7056  /**
7057  * Connect to Server Failed
7058  * @const 
7059  */
7060 Roo.form.Action.CONNECT_FAILURE = 'connect';
7061 /**
7062  * Reading Data from Server Failed
7063  * @const 
7064  */
7065 Roo.form.Action.LOAD_FAILURE = 'load';
7066
7067 Roo.form.Action.prototype = {
7068     type : 'default',
7069     failureType : undefined,
7070     response : undefined,
7071     result : undefined,
7072
7073     // interface method
7074     run : function(options){
7075
7076     },
7077
7078     // interface method
7079     success : function(response){
7080
7081     },
7082
7083     // interface method
7084     handleResponse : function(response){
7085
7086     },
7087
7088     // default connection failure
7089     failure : function(response){
7090         
7091         this.response = response;
7092         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7093         this.form.afterAction(this, false);
7094     },
7095
7096     processResponse : function(response){
7097         this.response = response;
7098         if(!response.responseText){
7099             return true;
7100         }
7101         this.result = this.handleResponse(response);
7102         return this.result;
7103     },
7104
7105     // utility functions used internally
7106     getUrl : function(appendParams){
7107         var url = this.options.url || this.form.url || this.form.el.dom.action;
7108         if(appendParams){
7109             var p = this.getParams();
7110             if(p){
7111                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7112             }
7113         }
7114         return url;
7115     },
7116
7117     getMethod : function(){
7118         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7119     },
7120
7121     getParams : function(){
7122         var bp = this.form.baseParams;
7123         var p = this.options.params;
7124         if(p){
7125             if(typeof p == "object"){
7126                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7127             }else if(typeof p == 'string' && bp){
7128                 p += '&' + Roo.urlEncode(bp);
7129             }
7130         }else if(bp){
7131             p = Roo.urlEncode(bp);
7132         }
7133         return p;
7134     },
7135
7136     createCallback : function(){
7137         return {
7138             success: this.success,
7139             failure: this.failure,
7140             scope: this,
7141             timeout: (this.form.timeout*1000),
7142             upload: this.form.fileUpload ? this.success : undefined
7143         };
7144     }
7145 };
7146
7147 Roo.form.Action.Submit = function(form, options){
7148     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7149 };
7150
7151 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7152     type : 'submit',
7153
7154     haveProgress : false,
7155     uploadComplete : false,
7156     
7157     // uploadProgress indicator.
7158     uploadProgress : function()
7159     {
7160         if (!this.form.progressUrl) {
7161             return;
7162         }
7163         
7164         if (!this.haveProgress) {
7165             Roo.MessageBox.progress("Uploading", "Uploading");
7166         }
7167         if (this.uploadComplete) {
7168            Roo.MessageBox.hide();
7169            return;
7170         }
7171         
7172         this.haveProgress = true;
7173    
7174         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7175         
7176         var c = new Roo.data.Connection();
7177         c.request({
7178             url : this.form.progressUrl,
7179             params: {
7180                 id : uid
7181             },
7182             method: 'GET',
7183             success : function(req){
7184                //console.log(data);
7185                 var rdata = false;
7186                 var edata;
7187                 try  {
7188                    rdata = Roo.decode(req.responseText)
7189                 } catch (e) {
7190                     Roo.log("Invalid data from server..");
7191                     Roo.log(edata);
7192                     return;
7193                 }
7194                 if (!rdata || !rdata.success) {
7195                     Roo.log(rdata);
7196                     Roo.MessageBox.alert(Roo.encode(rdata));
7197                     return;
7198                 }
7199                 var data = rdata.data;
7200                 
7201                 if (this.uploadComplete) {
7202                    Roo.MessageBox.hide();
7203                    return;
7204                 }
7205                    
7206                 if (data){
7207                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7208                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7209                     );
7210                 }
7211                 this.uploadProgress.defer(2000,this);
7212             },
7213        
7214             failure: function(data) {
7215                 Roo.log('progress url failed ');
7216                 Roo.log(data);
7217             },
7218             scope : this
7219         });
7220            
7221     },
7222     
7223     
7224     run : function()
7225     {
7226         // run get Values on the form, so it syncs any secondary forms.
7227         this.form.getValues();
7228         
7229         var o = this.options;
7230         var method = this.getMethod();
7231         var isPost = method == 'POST';
7232         if(o.clientValidation === false || this.form.isValid()){
7233             
7234             if (this.form.progressUrl) {
7235                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7236                     (new Date() * 1) + '' + Math.random());
7237                     
7238             } 
7239             
7240             
7241             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7242                 form:this.form.el.dom,
7243                 url:this.getUrl(!isPost),
7244                 method: method,
7245                 params:isPost ? this.getParams() : null,
7246                 isUpload: this.form.fileUpload
7247             }));
7248             
7249             this.uploadProgress();
7250
7251         }else if (o.clientValidation !== false){ // client validation failed
7252             this.failureType = Roo.form.Action.CLIENT_INVALID;
7253             this.form.afterAction(this, false);
7254         }
7255     },
7256
7257     success : function(response)
7258     {
7259         this.uploadComplete= true;
7260         if (this.haveProgress) {
7261             Roo.MessageBox.hide();
7262         }
7263         
7264         
7265         var result = this.processResponse(response);
7266         if(result === true || result.success){
7267             this.form.afterAction(this, true);
7268             return;
7269         }
7270         if(result.errors){
7271             this.form.markInvalid(result.errors);
7272             this.failureType = Roo.form.Action.SERVER_INVALID;
7273         }
7274         this.form.afterAction(this, false);
7275     },
7276     failure : function(response)
7277     {
7278         this.uploadComplete= true;
7279         if (this.haveProgress) {
7280             Roo.MessageBox.hide();
7281         }
7282         
7283         this.response = response;
7284         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7285         this.form.afterAction(this, false);
7286     },
7287     
7288     handleResponse : function(response){
7289         if(this.form.errorReader){
7290             var rs = this.form.errorReader.read(response);
7291             var errors = [];
7292             if(rs.records){
7293                 for(var i = 0, len = rs.records.length; i < len; i++) {
7294                     var r = rs.records[i];
7295                     errors[i] = r.data;
7296                 }
7297             }
7298             if(errors.length < 1){
7299                 errors = null;
7300             }
7301             return {
7302                 success : rs.success,
7303                 errors : errors
7304             };
7305         }
7306         var ret = false;
7307         try {
7308             ret = Roo.decode(response.responseText);
7309         } catch (e) {
7310             ret = {
7311                 success: false,
7312                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7313                 errors : []
7314             };
7315         }
7316         return ret;
7317         
7318     }
7319 });
7320
7321
7322 Roo.form.Action.Load = function(form, options){
7323     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7324     this.reader = this.form.reader;
7325 };
7326
7327 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7328     type : 'load',
7329
7330     run : function(){
7331         
7332         Roo.Ajax.request(Roo.apply(
7333                 this.createCallback(), {
7334                     method:this.getMethod(),
7335                     url:this.getUrl(false),
7336                     params:this.getParams()
7337         }));
7338     },
7339
7340     success : function(response){
7341         
7342         var result = this.processResponse(response);
7343         if(result === true || !result.success || !result.data){
7344             this.failureType = Roo.form.Action.LOAD_FAILURE;
7345             this.form.afterAction(this, false);
7346             return;
7347         }
7348         this.form.clearInvalid();
7349         this.form.setValues(result.data);
7350         this.form.afterAction(this, true);
7351     },
7352
7353     handleResponse : function(response){
7354         if(this.form.reader){
7355             var rs = this.form.reader.read(response);
7356             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7357             return {
7358                 success : rs.success,
7359                 data : data
7360             };
7361         }
7362         return Roo.decode(response.responseText);
7363     }
7364 });
7365
7366 Roo.form.Action.ACTION_TYPES = {
7367     'load' : Roo.form.Action.Load,
7368     'submit' : Roo.form.Action.Submit
7369 };/*
7370  * - LGPL
7371  *
7372  * form
7373  * 
7374  */
7375
7376 /**
7377  * @class Roo.bootstrap.Form
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Form class
7380  * @cfg {String} method  GET | POST (default POST)
7381  * @cfg {String} labelAlign top | left (default top)
7382  * @cfg {String} align left  | right - for navbars
7383  * @cfg {Boolean} loadMask load mask when submit (default true)
7384
7385  * 
7386  * @constructor
7387  * Create a new Form
7388  * @param {Object} config The config object
7389  */
7390
7391
7392 Roo.bootstrap.Form = function(config){
7393     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7394     this.addEvents({
7395         /**
7396          * @event clientvalidation
7397          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7398          * @param {Form} this
7399          * @param {Boolean} valid true if the form has passed client-side validation
7400          */
7401         clientvalidation: true,
7402         /**
7403          * @event beforeaction
7404          * Fires before any action is performed. Return false to cancel the action.
7405          * @param {Form} this
7406          * @param {Action} action The action to be performed
7407          */
7408         beforeaction: true,
7409         /**
7410          * @event actionfailed
7411          * Fires when an action fails.
7412          * @param {Form} this
7413          * @param {Action} action The action that failed
7414          */
7415         actionfailed : true,
7416         /**
7417          * @event actioncomplete
7418          * Fires when an action is completed.
7419          * @param {Form} this
7420          * @param {Action} action The action that completed
7421          */
7422         actioncomplete : true
7423     });
7424     
7425 };
7426
7427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7428       
7429      /**
7430      * @cfg {String} method
7431      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7432      */
7433     method : 'POST',
7434     /**
7435      * @cfg {String} url
7436      * The URL to use for form actions if one isn't supplied in the action options.
7437      */
7438     /**
7439      * @cfg {Boolean} fileUpload
7440      * Set to true if this form is a file upload.
7441      */
7442      
7443     /**
7444      * @cfg {Object} baseParams
7445      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7446      */
7447       
7448     /**
7449      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7450      */
7451     timeout: 30,
7452     /**
7453      * @cfg {Sting} align (left|right) for navbar forms
7454      */
7455     align : 'left',
7456
7457     // private
7458     activeAction : null,
7459  
7460     /**
7461      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7462      * element by passing it or its id or mask the form itself by passing in true.
7463      * @type Mixed
7464      */
7465     waitMsgTarget : false,
7466     
7467     loadMask : true,
7468     
7469     getAutoCreate : function(){
7470         
7471         var cfg = {
7472             tag: 'form',
7473             method : this.method || 'POST',
7474             id : this.id || Roo.id(),
7475             cls : ''
7476         };
7477         if (this.parent().xtype.match(/^Nav/)) {
7478             cfg.cls = 'navbar-form navbar-' + this.align;
7479             
7480         }
7481         
7482         if (this.labelAlign == 'left' ) {
7483             cfg.cls += ' form-horizontal';
7484         }
7485         
7486         
7487         return cfg;
7488     },
7489     initEvents : function()
7490     {
7491         this.el.on('submit', this.onSubmit, this);
7492         // this was added as random key presses on the form where triggering form submit.
7493         this.el.on('keypress', function(e) {
7494             if (e.getCharCode() != 13) {
7495                 return true;
7496             }
7497             // we might need to allow it for textareas.. and some other items.
7498             // check e.getTarget().
7499             
7500             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7501                 return true;
7502             }
7503         
7504             Roo.log("keypress blocked");
7505             
7506             e.preventDefault();
7507             return false;
7508         });
7509         
7510     },
7511     // private
7512     onSubmit : function(e){
7513         e.stopEvent();
7514     },
7515     
7516      /**
7517      * Returns true if client-side validation on the form is successful.
7518      * @return Boolean
7519      */
7520     isValid : function(){
7521         var items = this.getItems();
7522         var valid = true;
7523         items.each(function(f){
7524            if(!f.validate()){
7525                valid = false;
7526                
7527            }
7528         });
7529         return valid;
7530     },
7531     /**
7532      * Returns true if any fields in this form have changed since their original load.
7533      * @return Boolean
7534      */
7535     isDirty : function(){
7536         var dirty = false;
7537         var items = this.getItems();
7538         items.each(function(f){
7539            if(f.isDirty()){
7540                dirty = true;
7541                return false;
7542            }
7543            return true;
7544         });
7545         return dirty;
7546     },
7547      /**
7548      * Performs a predefined action (submit or load) or custom actions you define on this form.
7549      * @param {String} actionName The name of the action type
7550      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7551      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7552      * accept other config options):
7553      * <pre>
7554 Property          Type             Description
7555 ----------------  ---------------  ----------------------------------------------------------------------------------
7556 url               String           The url for the action (defaults to the form's url)
7557 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7558 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7559 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7560                                    validate the form on the client (defaults to false)
7561      * </pre>
7562      * @return {BasicForm} this
7563      */
7564     doAction : function(action, options){
7565         if(typeof action == 'string'){
7566             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7567         }
7568         if(this.fireEvent('beforeaction', this, action) !== false){
7569             this.beforeAction(action);
7570             action.run.defer(100, action);
7571         }
7572         return this;
7573     },
7574     
7575     // private
7576     beforeAction : function(action){
7577         var o = action.options;
7578         
7579         if(this.loadMask){
7580             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7581         }
7582         // not really supported yet.. ??
7583         
7584         //if(this.waitMsgTarget === true){
7585         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586         //}else if(this.waitMsgTarget){
7587         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7588         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7589         //}else {
7590         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7591        // }
7592          
7593     },
7594
7595     // private
7596     afterAction : function(action, success){
7597         this.activeAction = null;
7598         var o = action.options;
7599         
7600         //if(this.waitMsgTarget === true){
7601             this.el.unmask();
7602         //}else if(this.waitMsgTarget){
7603         //    this.waitMsgTarget.unmask();
7604         //}else{
7605         //    Roo.MessageBox.updateProgress(1);
7606         //    Roo.MessageBox.hide();
7607        // }
7608         // 
7609         if(success){
7610             if(o.reset){
7611                 this.reset();
7612             }
7613             Roo.callback(o.success, o.scope, [this, action]);
7614             this.fireEvent('actioncomplete', this, action);
7615             
7616         }else{
7617             
7618             // failure condition..
7619             // we have a scenario where updates need confirming.
7620             // eg. if a locking scenario exists..
7621             // we look for { errors : { needs_confirm : true }} in the response.
7622             if (
7623                 (typeof(action.result) != 'undefined')  &&
7624                 (typeof(action.result.errors) != 'undefined')  &&
7625                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7626            ){
7627                 var _t = this;
7628                 Roo.log("not supported yet");
7629                  /*
7630                 
7631                 Roo.MessageBox.confirm(
7632                     "Change requires confirmation",
7633                     action.result.errorMsg,
7634                     function(r) {
7635                         if (r != 'yes') {
7636                             return;
7637                         }
7638                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7639                     }
7640                     
7641                 );
7642                 */
7643                 
7644                 
7645                 return;
7646             }
7647             
7648             Roo.callback(o.failure, o.scope, [this, action]);
7649             // show an error message if no failed handler is set..
7650             if (!this.hasListener('actionfailed')) {
7651                 Roo.log("need to add dialog support");
7652                 /*
7653                 Roo.MessageBox.alert("Error",
7654                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7655                         action.result.errorMsg :
7656                         "Saving Failed, please check your entries or try again"
7657                 );
7658                 */
7659             }
7660             
7661             this.fireEvent('actionfailed', this, action);
7662         }
7663         
7664     },
7665     /**
7666      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7667      * @param {String} id The value to search for
7668      * @return Field
7669      */
7670     findField : function(id){
7671         var items = this.getItems();
7672         var field = items.get(id);
7673         if(!field){
7674              items.each(function(f){
7675                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7676                     field = f;
7677                     return false;
7678                 }
7679                 return true;
7680             });
7681         }
7682         return field || null;
7683     },
7684      /**
7685      * Mark fields in this form invalid in bulk.
7686      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7687      * @return {BasicForm} this
7688      */
7689     markInvalid : function(errors){
7690         if(errors instanceof Array){
7691             for(var i = 0, len = errors.length; i < len; i++){
7692                 var fieldError = errors[i];
7693                 var f = this.findField(fieldError.id);
7694                 if(f){
7695                     f.markInvalid(fieldError.msg);
7696                 }
7697             }
7698         }else{
7699             var field, id;
7700             for(id in errors){
7701                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7702                     field.markInvalid(errors[id]);
7703                 }
7704             }
7705         }
7706         //Roo.each(this.childForms || [], function (f) {
7707         //    f.markInvalid(errors);
7708         //});
7709         
7710         return this;
7711     },
7712
7713     /**
7714      * Set values for fields in this form in bulk.
7715      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7716      * @return {BasicForm} this
7717      */
7718     setValues : function(values){
7719         if(values instanceof Array){ // array of objects
7720             for(var i = 0, len = values.length; i < len; i++){
7721                 var v = values[i];
7722                 var f = this.findField(v.id);
7723                 if(f){
7724                     f.setValue(v.value);
7725                     if(this.trackResetOnLoad){
7726                         f.originalValue = f.getValue();
7727                     }
7728                 }
7729             }
7730         }else{ // object hash
7731             var field, id;
7732             for(id in values){
7733                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7734                     
7735                     if (field.setFromData && 
7736                         field.valueField && 
7737                         field.displayField &&
7738                         // combos' with local stores can 
7739                         // be queried via setValue()
7740                         // to set their value..
7741                         (field.store && !field.store.isLocal)
7742                         ) {
7743                         // it's a combo
7744                         var sd = { };
7745                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7746                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7747                         field.setFromData(sd);
7748                         
7749                     } else {
7750                         field.setValue(values[id]);
7751                     }
7752                     
7753                     
7754                     if(this.trackResetOnLoad){
7755                         field.originalValue = field.getValue();
7756                     }
7757                 }
7758             }
7759         }
7760          
7761         //Roo.each(this.childForms || [], function (f) {
7762         //    f.setValues(values);
7763         //});
7764                 
7765         return this;
7766     },
7767
7768     /**
7769      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7770      * they are returned as an array.
7771      * @param {Boolean} asString
7772      * @return {Object}
7773      */
7774     getValues : function(asString){
7775         //if (this.childForms) {
7776             // copy values from the child forms
7777         //    Roo.each(this.childForms, function (f) {
7778         //        this.setValues(f.getValues());
7779         //    }, this);
7780         //}
7781         
7782         
7783         
7784         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7785         if(asString === true){
7786             return fs;
7787         }
7788         return Roo.urlDecode(fs);
7789     },
7790     
7791     /**
7792      * Returns the fields in this form as an object with key/value pairs. 
7793      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7794      * @return {Object}
7795      */
7796     getFieldValues : function(with_hidden)
7797     {
7798         var items = this.getItems();
7799         var ret = {};
7800         items.each(function(f){
7801             if (!f.getName()) {
7802                 return;
7803             }
7804             var v = f.getValue();
7805             if (f.inputType =='radio') {
7806                 if (typeof(ret[f.getName()]) == 'undefined') {
7807                     ret[f.getName()] = ''; // empty..
7808                 }
7809                 
7810                 if (!f.el.dom.checked) {
7811                     return;
7812                     
7813                 }
7814                 v = f.el.dom.value;
7815                 
7816             }
7817             
7818             // not sure if this supported any more..
7819             if ((typeof(v) == 'object') && f.getRawValue) {
7820                 v = f.getRawValue() ; // dates..
7821             }
7822             // combo boxes where name != hiddenName...
7823             if (f.name != f.getName()) {
7824                 ret[f.name] = f.getRawValue();
7825             }
7826             ret[f.getName()] = v;
7827         });
7828         
7829         return ret;
7830     },
7831
7832     /**
7833      * Clears all invalid messages in this form.
7834      * @return {BasicForm} this
7835      */
7836     clearInvalid : function(){
7837         var items = this.getItems();
7838         
7839         items.each(function(f){
7840            f.clearInvalid();
7841         });
7842         
7843         
7844         
7845         return this;
7846     },
7847
7848     /**
7849      * Resets this form.
7850      * @return {BasicForm} this
7851      */
7852     reset : function(){
7853         var items = this.getItems();
7854         items.each(function(f){
7855             f.reset();
7856         });
7857         
7858         Roo.each(this.childForms || [], function (f) {
7859             f.reset();
7860         });
7861        
7862         
7863         return this;
7864     },
7865     getItems : function()
7866     {
7867         var r=new Roo.util.MixedCollection(false, function(o){
7868             return o.id || (o.id = Roo.id());
7869         });
7870         var iter = function(el) {
7871             if (el.inputEl) {
7872                 r.add(el);
7873             }
7874             if (!el.items) {
7875                 return;
7876             }
7877             Roo.each(el.items,function(e) {
7878                 iter(e);
7879             });
7880             
7881             
7882         };
7883         
7884         iter(this);
7885         return r;
7886         
7887         
7888         
7889         
7890     }
7891     
7892 });
7893
7894  
7895 /*
7896  * Based on:
7897  * Ext JS Library 1.1.1
7898  * Copyright(c) 2006-2007, Ext JS, LLC.
7899  *
7900  * Originally Released Under LGPL - original licence link has changed is not relivant.
7901  *
7902  * Fork - LGPL
7903  * <script type="text/javascript">
7904  */
7905 /**
7906  * @class Roo.form.VTypes
7907  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7908  * @singleton
7909  */
7910 Roo.form.VTypes = function(){
7911     // closure these in so they are only created once.
7912     var alpha = /^[a-zA-Z_]+$/;
7913     var alphanum = /^[a-zA-Z0-9_]+$/;
7914     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7915     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7916
7917     // All these messages and functions are configurable
7918     return {
7919         /**
7920          * The function used to validate email addresses
7921          * @param {String} value The email address
7922          */
7923         'email' : function(v){
7924             return email.test(v);
7925         },
7926         /**
7927          * The error text to display when the email validation function returns false
7928          * @type String
7929          */
7930         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7931         /**
7932          * The keystroke filter mask to be applied on email input
7933          * @type RegExp
7934          */
7935         'emailMask' : /[a-z0-9_\.\-@]/i,
7936
7937         /**
7938          * The function used to validate URLs
7939          * @param {String} value The URL
7940          */
7941         'url' : function(v){
7942             return url.test(v);
7943         },
7944         /**
7945          * The error text to display when the url validation function returns false
7946          * @type String
7947          */
7948         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7949         
7950         /**
7951          * The function used to validate alpha values
7952          * @param {String} value The value
7953          */
7954         'alpha' : function(v){
7955             return alpha.test(v);
7956         },
7957         /**
7958          * The error text to display when the alpha validation function returns false
7959          * @type String
7960          */
7961         'alphaText' : 'This field should only contain letters and _',
7962         /**
7963          * The keystroke filter mask to be applied on alpha input
7964          * @type RegExp
7965          */
7966         'alphaMask' : /[a-z_]/i,
7967
7968         /**
7969          * The function used to validate alphanumeric values
7970          * @param {String} value The value
7971          */
7972         'alphanum' : function(v){
7973             return alphanum.test(v);
7974         },
7975         /**
7976          * The error text to display when the alphanumeric validation function returns false
7977          * @type String
7978          */
7979         'alphanumText' : 'This field should only contain letters, numbers and _',
7980         /**
7981          * The keystroke filter mask to be applied on alphanumeric input
7982          * @type RegExp
7983          */
7984         'alphanumMask' : /[a-z0-9_]/i
7985     };
7986 }();/*
7987  * - LGPL
7988  *
7989  * Input
7990  * 
7991  */
7992
7993 /**
7994  * @class Roo.bootstrap.Input
7995  * @extends Roo.bootstrap.Component
7996  * Bootstrap Input class
7997  * @cfg {Boolean} disabled is it disabled
7998  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7999  * @cfg {String} name name of the input
8000  * @cfg {string} fieldLabel - the label associated
8001  * @cfg {string} placeholder - placeholder to put in text.
8002  * @cfg {string}  before - input group add on before
8003  * @cfg {string} after - input group add on after
8004  * @cfg {string} size - (lg|sm) or leave empty..
8005  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8006  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8007  * @cfg {Number} md colspan out of 12 for computer-sized screens
8008  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8009  * @cfg {string} value default value of the input
8010  * @cfg {Number} labelWidth set the width of label (0-12)
8011  * @cfg {String} labelAlign (top|left)
8012  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8013  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8014  * @cfg {String} indicatorpos (left|right) default left
8015
8016  * @cfg {String} align (left|center|right) Default left
8017  * @cfg {Boolean} forceFeedback (true|false) Default false
8018  * 
8019  * 
8020  * 
8021  * 
8022  * @constructor
8023  * Create a new Input
8024  * @param {Object} config The config object
8025  */
8026
8027 Roo.bootstrap.Input = function(config){
8028     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8029    
8030         this.addEvents({
8031             /**
8032              * @event focus
8033              * Fires when this field receives input focus.
8034              * @param {Roo.form.Field} this
8035              */
8036             focus : true,
8037             /**
8038              * @event blur
8039              * Fires when this field loses input focus.
8040              * @param {Roo.form.Field} this
8041              */
8042             blur : true,
8043             /**
8044              * @event specialkey
8045              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8046              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8047              * @param {Roo.form.Field} this
8048              * @param {Roo.EventObject} e The event object
8049              */
8050             specialkey : true,
8051             /**
8052              * @event change
8053              * Fires just before the field blurs if the field value has changed.
8054              * @param {Roo.form.Field} this
8055              * @param {Mixed} newValue The new value
8056              * @param {Mixed} oldValue The original value
8057              */
8058             change : true,
8059             /**
8060              * @event invalid
8061              * Fires after the field has been marked as invalid.
8062              * @param {Roo.form.Field} this
8063              * @param {String} msg The validation message
8064              */
8065             invalid : true,
8066             /**
8067              * @event valid
8068              * Fires after the field has been validated with no errors.
8069              * @param {Roo.form.Field} this
8070              */
8071             valid : true,
8072              /**
8073              * @event keyup
8074              * Fires after the key up
8075              * @param {Roo.form.Field} this
8076              * @param {Roo.EventObject}  e The event Object
8077              */
8078             keyup : true
8079         });
8080 };
8081
8082 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8083      /**
8084      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8085       automatic validation (defaults to "keyup").
8086      */
8087     validationEvent : "keyup",
8088      /**
8089      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8090      */
8091     validateOnBlur : true,
8092     /**
8093      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8094      */
8095     validationDelay : 250,
8096      /**
8097      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8098      */
8099     focusClass : "x-form-focus",  // not needed???
8100     
8101        
8102     /**
8103      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8104      */
8105     invalidClass : "has-warning",
8106     
8107     /**
8108      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8109      */
8110     validClass : "has-success",
8111     
8112     /**
8113      * @cfg {Boolean} hasFeedback (true|false) default true
8114      */
8115     hasFeedback : true,
8116     
8117     /**
8118      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8119      */
8120     invalidFeedbackClass : "glyphicon-warning-sign",
8121     
8122     /**
8123      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8124      */
8125     validFeedbackClass : "glyphicon-ok",
8126     
8127     /**
8128      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8129      */
8130     selectOnFocus : false,
8131     
8132      /**
8133      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8134      */
8135     maskRe : null,
8136        /**
8137      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8138      */
8139     vtype : null,
8140     
8141       /**
8142      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8143      */
8144     disableKeyFilter : false,
8145     
8146        /**
8147      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8148      */
8149     disabled : false,
8150      /**
8151      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8152      */
8153     allowBlank : true,
8154     /**
8155      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8156      */
8157     blankText : "This field is required",
8158     
8159      /**
8160      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8161      */
8162     minLength : 0,
8163     /**
8164      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8165      */
8166     maxLength : Number.MAX_VALUE,
8167     /**
8168      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8169      */
8170     minLengthText : "The minimum length for this field is {0}",
8171     /**
8172      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8173      */
8174     maxLengthText : "The maximum length for this field is {0}",
8175   
8176     
8177     /**
8178      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8179      * If available, this function will be called only after the basic validators all return true, and will be passed the
8180      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8181      */
8182     validator : null,
8183     /**
8184      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8185      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8186      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8187      */
8188     regex : null,
8189     /**
8190      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8191      */
8192     regexText : "",
8193     
8194     autocomplete: false,
8195     
8196     
8197     fieldLabel : '',
8198     inputType : 'text',
8199     
8200     name : false,
8201     placeholder: false,
8202     before : false,
8203     after : false,
8204     size : false,
8205     hasFocus : false,
8206     preventMark: false,
8207     isFormField : true,
8208     value : '',
8209     labelWidth : 2,
8210     labelAlign : false,
8211     readOnly : false,
8212     align : false,
8213     formatedValue : false,
8214     forceFeedback : false,
8215     
8216     indicatorpos : 'left',
8217     
8218     parentLabelAlign : function()
8219     {
8220         var parent = this;
8221         while (parent.parent()) {
8222             parent = parent.parent();
8223             if (typeof(parent.labelAlign) !='undefined') {
8224                 return parent.labelAlign;
8225             }
8226         }
8227         return 'left';
8228         
8229     },
8230     
8231     getAutoCreate : function()
8232     {
8233         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8234         
8235         var id = Roo.id();
8236         
8237         var cfg = {};
8238         
8239         if(this.inputType != 'hidden'){
8240             cfg.cls = 'form-group' //input-group
8241         }
8242         
8243         var input =  {
8244             tag: 'input',
8245             id : id,
8246             type : this.inputType,
8247             value : this.value,
8248             cls : 'form-control',
8249             placeholder : this.placeholder || '',
8250             autocomplete : this.autocomplete || 'new-password'
8251         };
8252         
8253         if(this.align){
8254             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8255         }
8256         
8257         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8258             input.maxLength = this.maxLength;
8259         }
8260         
8261         if (this.disabled) {
8262             input.disabled=true;
8263         }
8264         
8265         if (this.readOnly) {
8266             input.readonly=true;
8267         }
8268         
8269         if (this.name) {
8270             input.name = this.name;
8271         }
8272         
8273         if (this.size) {
8274             input.cls += ' input-' + this.size;
8275         }
8276         
8277         var settings=this;
8278         ['xs','sm','md','lg'].map(function(size){
8279             if (settings[size]) {
8280                 cfg.cls += ' col-' + size + '-' + settings[size];
8281             }
8282         });
8283         
8284         var inputblock = input;
8285         
8286         var feedback = {
8287             tag: 'span',
8288             cls: 'glyphicon form-control-feedback'
8289         };
8290             
8291         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8292             
8293             inputblock = {
8294                 cls : 'has-feedback',
8295                 cn :  [
8296                     input,
8297                     feedback
8298                 ] 
8299             };  
8300         }
8301         
8302         if (this.before || this.after) {
8303             
8304             inputblock = {
8305                 cls : 'input-group',
8306                 cn :  [] 
8307             };
8308             
8309             if (this.before && typeof(this.before) == 'string') {
8310                 
8311                 inputblock.cn.push({
8312                     tag :'span',
8313                     cls : 'roo-input-before input-group-addon',
8314                     html : this.before
8315                 });
8316             }
8317             if (this.before && typeof(this.before) == 'object') {
8318                 this.before = Roo.factory(this.before);
8319                 
8320                 inputblock.cn.push({
8321                     tag :'span',
8322                     cls : 'roo-input-before input-group-' +
8323                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8324                 });
8325             }
8326             
8327             inputblock.cn.push(input);
8328             
8329             if (this.after && typeof(this.after) == 'string') {
8330                 inputblock.cn.push({
8331                     tag :'span',
8332                     cls : 'roo-input-after input-group-addon',
8333                     html : this.after
8334                 });
8335             }
8336             if (this.after && typeof(this.after) == 'object') {
8337                 this.after = Roo.factory(this.after);
8338                 
8339                 inputblock.cn.push({
8340                     tag :'span',
8341                     cls : 'roo-input-after input-group-' +
8342                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8343                 });
8344             }
8345             
8346             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8347                 inputblock.cls += ' has-feedback';
8348                 inputblock.cn.push(feedback);
8349             }
8350         };
8351         
8352         if (align ==='left' && this.fieldLabel.length) {
8353             
8354             cfg.cn = [
8355                 {
8356                     tag : 'i',
8357                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8358                     tooltip : 'This field is required'
8359                 },
8360                 {
8361                     tag: 'label',
8362                     'for' :  id,
8363                     cls : 'control-label col-sm-' + this.labelWidth,
8364                     html : this.fieldLabel
8365
8366                 },
8367                 {
8368                     cls : "col-sm-" + (12 - this.labelWidth), 
8369                     cn: [
8370                         inputblock
8371                     ]
8372                 }
8373
8374             ];
8375             
8376             if(this.indicatorpos == 'right'){
8377                 cfg.cn = [
8378                     {
8379                         tag: 'label',
8380                         'for' :  id,
8381                         cls : 'control-label col-sm-' + this.labelWidth,
8382                         html : this.fieldLabel
8383
8384                     },
8385                     {
8386                         tag : 'i',
8387                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8388                         tooltip : 'This field is required'
8389                     },
8390                     {
8391                         cls : "col-sm-" + (12 - this.labelWidth), 
8392                         cn: [
8393                             inputblock
8394                         ]
8395                     }
8396
8397                 ];
8398             }
8399             
8400         } else if ( this.fieldLabel.length) {
8401                 
8402             cfg.cn = [
8403                 {
8404                     tag : 'i',
8405                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8406                     tooltip : 'This field is required'
8407                 },
8408                 {
8409                     tag: 'label',
8410                    //cls : 'input-group-addon',
8411                     html : this.fieldLabel
8412
8413                 },
8414
8415                inputblock
8416
8417            ];
8418            
8419            if(this.indicatorpos == 'right'){
8420                 
8421                 cfg.cn = [
8422                     {
8423                         tag: 'label',
8424                        //cls : 'input-group-addon',
8425                         html : this.fieldLabel
8426
8427                     },
8428                     {
8429                         tag : 'i',
8430                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8431                         tooltip : 'This field is required'
8432                     },
8433
8434                    inputblock
8435
8436                ];
8437
8438             }
8439
8440         } else {
8441             
8442             cfg.cn = [
8443
8444                     inputblock
8445
8446             ];
8447                 
8448                 
8449         };
8450         
8451         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8452            cfg.cls += ' navbar-form';
8453         }
8454         
8455         if (this.parentType === 'NavGroup') {
8456            cfg.cls += ' navbar-form';
8457            cfg.tag = 'li';
8458         }
8459         
8460         return cfg;
8461         
8462     },
8463     /**
8464      * return the real input element.
8465      */
8466     inputEl: function ()
8467     {
8468         return this.el.select('input.form-control',true).first();
8469     },
8470     
8471     tooltipEl : function()
8472     {
8473         return this.inputEl();
8474     },
8475     
8476     indicatorEl : function()
8477     {
8478         var indicator = this.el.select('i.roo-required-indicator',true).first();
8479         
8480         if(!indicator){
8481             return false;
8482         }
8483         
8484         return indicator;
8485         
8486     },
8487     
8488     setDisabled : function(v)
8489     {
8490         var i  = this.inputEl().dom;
8491         if (!v) {
8492             i.removeAttribute('disabled');
8493             return;
8494             
8495         }
8496         i.setAttribute('disabled','true');
8497     },
8498     initEvents : function()
8499     {
8500           
8501         this.inputEl().on("keydown" , this.fireKey,  this);
8502         this.inputEl().on("focus", this.onFocus,  this);
8503         this.inputEl().on("blur", this.onBlur,  this);
8504         
8505         this.inputEl().relayEvent('keyup', this);
8506         
8507         this.indicator = this.indicatorEl();
8508         
8509         if(this.indicator){
8510             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8511             this.indicator.hide();
8512         }
8513  
8514         // reference to original value for reset
8515         this.originalValue = this.getValue();
8516         //Roo.form.TextField.superclass.initEvents.call(this);
8517         if(this.validationEvent == 'keyup'){
8518             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8519             this.inputEl().on('keyup', this.filterValidation, this);
8520         }
8521         else if(this.validationEvent !== false){
8522             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8523         }
8524         
8525         if(this.selectOnFocus){
8526             this.on("focus", this.preFocus, this);
8527             
8528         }
8529         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8530             this.inputEl().on("keypress", this.filterKeys, this);
8531         }
8532        /* if(this.grow){
8533             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8534             this.el.on("click", this.autoSize,  this);
8535         }
8536         */
8537         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8538             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8539         }
8540         
8541         if (typeof(this.before) == 'object') {
8542             this.before.render(this.el.select('.roo-input-before',true).first());
8543         }
8544         if (typeof(this.after) == 'object') {
8545             this.after.render(this.el.select('.roo-input-after',true).first());
8546         }
8547         
8548         
8549     },
8550     filterValidation : function(e){
8551         if(!e.isNavKeyPress()){
8552             this.validationTask.delay(this.validationDelay);
8553         }
8554     },
8555      /**
8556      * Validates the field value
8557      * @return {Boolean} True if the value is valid, else false
8558      */
8559     validate : function(){
8560         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8561         if(this.disabled || this.validateValue(this.getRawValue())){
8562             this.markValid();
8563             return true;
8564         }
8565         
8566         this.markInvalid();
8567         return false;
8568     },
8569     
8570     
8571     /**
8572      * Validates a value according to the field's validation rules and marks the field as invalid
8573      * if the validation fails
8574      * @param {Mixed} value The value to validate
8575      * @return {Boolean} True if the value is valid, else false
8576      */
8577     validateValue : function(value){
8578         if(value.length < 1)  { // if it's blank
8579             if(this.allowBlank){
8580                 return true;
8581             }
8582             return false;
8583         }
8584         
8585         if(value.length < this.minLength){
8586             return false;
8587         }
8588         if(value.length > this.maxLength){
8589             return false;
8590         }
8591         if(this.vtype){
8592             var vt = Roo.form.VTypes;
8593             if(!vt[this.vtype](value, this)){
8594                 return false;
8595             }
8596         }
8597         if(typeof this.validator == "function"){
8598             var msg = this.validator(value);
8599             if(msg !== true){
8600                 return false;
8601             }
8602         }
8603         
8604         if(this.regex && !this.regex.test(value)){
8605             return false;
8606         }
8607         
8608         return true;
8609     },
8610
8611     
8612     
8613      // private
8614     fireKey : function(e){
8615         //Roo.log('field ' + e.getKey());
8616         if(e.isNavKeyPress()){
8617             this.fireEvent("specialkey", this, e);
8618         }
8619     },
8620     focus : function (selectText){
8621         if(this.rendered){
8622             this.inputEl().focus();
8623             if(selectText === true){
8624                 this.inputEl().dom.select();
8625             }
8626         }
8627         return this;
8628     } ,
8629     
8630     onFocus : function(){
8631         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8632            // this.el.addClass(this.focusClass);
8633         }
8634         if(!this.hasFocus){
8635             this.hasFocus = true;
8636             this.startValue = this.getValue();
8637             this.fireEvent("focus", this);
8638         }
8639     },
8640     
8641     beforeBlur : Roo.emptyFn,
8642
8643     
8644     // private
8645     onBlur : function(){
8646         this.beforeBlur();
8647         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8648             //this.el.removeClass(this.focusClass);
8649         }
8650         this.hasFocus = false;
8651         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8652             this.validate();
8653         }
8654         var v = this.getValue();
8655         if(String(v) !== String(this.startValue)){
8656             this.fireEvent('change', this, v, this.startValue);
8657         }
8658         this.fireEvent("blur", this);
8659     },
8660     
8661     /**
8662      * Resets the current field value to the originally loaded value and clears any validation messages
8663      */
8664     reset : function(){
8665         this.setValue(this.originalValue);
8666         this.validate();
8667     },
8668      /**
8669      * Returns the name of the field
8670      * @return {Mixed} name The name field
8671      */
8672     getName: function(){
8673         return this.name;
8674     },
8675      /**
8676      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8677      * @return {Mixed} value The field value
8678      */
8679     getValue : function(){
8680         
8681         var v = this.inputEl().getValue();
8682         
8683         return v;
8684     },
8685     /**
8686      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8687      * @return {Mixed} value The field value
8688      */
8689     getRawValue : function(){
8690         var v = this.inputEl().getValue();
8691         
8692         return v;
8693     },
8694     
8695     /**
8696      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8697      * @param {Mixed} value The value to set
8698      */
8699     setRawValue : function(v){
8700         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8701     },
8702     
8703     selectText : function(start, end){
8704         var v = this.getRawValue();
8705         if(v.length > 0){
8706             start = start === undefined ? 0 : start;
8707             end = end === undefined ? v.length : end;
8708             var d = this.inputEl().dom;
8709             if(d.setSelectionRange){
8710                 d.setSelectionRange(start, end);
8711             }else if(d.createTextRange){
8712                 var range = d.createTextRange();
8713                 range.moveStart("character", start);
8714                 range.moveEnd("character", v.length-end);
8715                 range.select();
8716             }
8717         }
8718     },
8719     
8720     /**
8721      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8722      * @param {Mixed} value The value to set
8723      */
8724     setValue : function(v){
8725         this.value = v;
8726         if(this.rendered){
8727             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8728             this.validate();
8729         }
8730     },
8731     
8732     /*
8733     processValue : function(value){
8734         if(this.stripCharsRe){
8735             var newValue = value.replace(this.stripCharsRe, '');
8736             if(newValue !== value){
8737                 this.setRawValue(newValue);
8738                 return newValue;
8739             }
8740         }
8741         return value;
8742     },
8743   */
8744     preFocus : function(){
8745         
8746         if(this.selectOnFocus){
8747             this.inputEl().dom.select();
8748         }
8749     },
8750     filterKeys : function(e){
8751         var k = e.getKey();
8752         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8753             return;
8754         }
8755         var c = e.getCharCode(), cc = String.fromCharCode(c);
8756         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8757             return;
8758         }
8759         if(!this.maskRe.test(cc)){
8760             e.stopEvent();
8761         }
8762     },
8763      /**
8764      * Clear any invalid styles/messages for this field
8765      */
8766     clearInvalid : function(){
8767         
8768         if(!this.el || this.preventMark){ // not rendered
8769             return;
8770         }
8771         
8772         if(this.indicator){
8773             this.indicator.hide();
8774         }
8775         
8776         this.el.removeClass(this.invalidClass);
8777         
8778         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8779             
8780             var feedback = this.el.select('.form-control-feedback', true).first();
8781             
8782             if(feedback){
8783                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8784             }
8785             
8786         }
8787         
8788         this.fireEvent('valid', this);
8789     },
8790     
8791      /**
8792      * Mark this field as valid
8793      */
8794     markValid : function()
8795     {
8796         if(!this.el  || this.preventMark){ // not rendered
8797             return;
8798         }
8799         
8800         this.el.removeClass([this.invalidClass, this.validClass]);
8801         
8802         var feedback = this.el.select('.form-control-feedback', true).first();
8803             
8804         if(feedback){
8805             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8806         }
8807
8808         if(this.disabled || this.allowBlank){
8809             return;
8810         }
8811         
8812         if(this.indicator){
8813             this.indicator.hide();
8814         }
8815         
8816         this.el.addClass(this.validClass);
8817         
8818         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8819             
8820             var feedback = this.el.select('.form-control-feedback', true).first();
8821             
8822             if(feedback){
8823                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8824                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8825             }
8826             
8827         }
8828         
8829         this.fireEvent('valid', this);
8830     },
8831     
8832      /**
8833      * Mark this field as invalid
8834      * @param {String} msg The validation message
8835      */
8836     markInvalid : function(msg)
8837     {
8838         if(!this.el  || this.preventMark){ // not rendered
8839             return;
8840         }
8841         
8842         this.el.removeClass([this.invalidClass, this.validClass]);
8843         
8844         var feedback = this.el.select('.form-control-feedback', true).first();
8845             
8846         if(feedback){
8847             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8848         }
8849
8850         if(this.disabled || this.allowBlank){
8851             return;
8852         }
8853         
8854         if(this.indicator){
8855             this.indicator.show();
8856         }
8857         
8858         this.el.addClass(this.invalidClass);
8859         
8860         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8861             
8862             var feedback = this.el.select('.form-control-feedback', true).first();
8863             
8864             if(feedback){
8865                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8866                 
8867                 if(this.getValue().length || this.forceFeedback){
8868                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8869                 }
8870                 
8871             }
8872             
8873         }
8874         
8875         this.fireEvent('invalid', this, msg);
8876     },
8877     // private
8878     SafariOnKeyDown : function(event)
8879     {
8880         // this is a workaround for a password hang bug on chrome/ webkit.
8881         
8882         var isSelectAll = false;
8883         
8884         if(this.inputEl().dom.selectionEnd > 0){
8885             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8886         }
8887         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8888             event.preventDefault();
8889             this.setValue('');
8890             return;
8891         }
8892         
8893         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8894             
8895             event.preventDefault();
8896             // this is very hacky as keydown always get's upper case.
8897             //
8898             var cc = String.fromCharCode(event.getCharCode());
8899             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8900             
8901         }
8902     },
8903     adjustWidth : function(tag, w){
8904         tag = tag.toLowerCase();
8905         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8906             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8907                 if(tag == 'input'){
8908                     return w + 2;
8909                 }
8910                 if(tag == 'textarea'){
8911                     return w-2;
8912                 }
8913             }else if(Roo.isOpera){
8914                 if(tag == 'input'){
8915                     return w + 2;
8916                 }
8917                 if(tag == 'textarea'){
8918                     return w-2;
8919                 }
8920             }
8921         }
8922         return w;
8923     }
8924     
8925 });
8926
8927  
8928 /*
8929  * - LGPL
8930  *
8931  * Input
8932  * 
8933  */
8934
8935 /**
8936  * @class Roo.bootstrap.TextArea
8937  * @extends Roo.bootstrap.Input
8938  * Bootstrap TextArea class
8939  * @cfg {Number} cols Specifies the visible width of a text area
8940  * @cfg {Number} rows Specifies the visible number of lines in a text area
8941  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8942  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8943  * @cfg {string} html text
8944  * 
8945  * @constructor
8946  * Create a new TextArea
8947  * @param {Object} config The config object
8948  */
8949
8950 Roo.bootstrap.TextArea = function(config){
8951     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8952    
8953 };
8954
8955 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8956      
8957     cols : false,
8958     rows : 5,
8959     readOnly : false,
8960     warp : 'soft',
8961     resize : false,
8962     value: false,
8963     html: false,
8964     
8965     getAutoCreate : function(){
8966         
8967         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8968         
8969         var id = Roo.id();
8970         
8971         var cfg = {};
8972         
8973         var input =  {
8974             tag: 'textarea',
8975             id : id,
8976             warp : this.warp,
8977             rows : this.rows,
8978             value : this.value || '',
8979             html: this.html || '',
8980             cls : 'form-control',
8981             placeholder : this.placeholder || '' 
8982             
8983         };
8984         
8985         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8986             input.maxLength = this.maxLength;
8987         }
8988         
8989         if(this.resize){
8990             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8991         }
8992         
8993         if(this.cols){
8994             input.cols = this.cols;
8995         }
8996         
8997         if (this.readOnly) {
8998             input.readonly = true;
8999         }
9000         
9001         if (this.name) {
9002             input.name = this.name;
9003         }
9004         
9005         if (this.size) {
9006             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9007         }
9008         
9009         var settings=this;
9010         ['xs','sm','md','lg'].map(function(size){
9011             if (settings[size]) {
9012                 cfg.cls += ' col-' + size + '-' + settings[size];
9013             }
9014         });
9015         
9016         var inputblock = input;
9017         
9018         if(this.hasFeedback && !this.allowBlank){
9019             
9020             var feedback = {
9021                 tag: 'span',
9022                 cls: 'glyphicon form-control-feedback'
9023             };
9024
9025             inputblock = {
9026                 cls : 'has-feedback',
9027                 cn :  [
9028                     input,
9029                     feedback
9030                 ] 
9031             };  
9032         }
9033         
9034         
9035         if (this.before || this.after) {
9036             
9037             inputblock = {
9038                 cls : 'input-group',
9039                 cn :  [] 
9040             };
9041             if (this.before) {
9042                 inputblock.cn.push({
9043                     tag :'span',
9044                     cls : 'input-group-addon',
9045                     html : this.before
9046                 });
9047             }
9048             
9049             inputblock.cn.push(input);
9050             
9051             if(this.hasFeedback && !this.allowBlank){
9052                 inputblock.cls += ' has-feedback';
9053                 inputblock.cn.push(feedback);
9054             }
9055             
9056             if (this.after) {
9057                 inputblock.cn.push({
9058                     tag :'span',
9059                     cls : 'input-group-addon',
9060                     html : this.after
9061                 });
9062             }
9063             
9064         }
9065         
9066         if (align ==='left' && this.fieldLabel.length) {
9067 //                Roo.log("left and has label");
9068                 cfg.cn = [
9069                     
9070                     {
9071                         tag: 'label',
9072                         'for' :  id,
9073                         cls : 'control-label col-sm-' + this.labelWidth,
9074                         html : this.fieldLabel
9075                         
9076                     },
9077                     {
9078                         cls : "col-sm-" + (12 - this.labelWidth), 
9079                         cn: [
9080                             inputblock
9081                         ]
9082                     }
9083                     
9084                 ];
9085         } else if ( this.fieldLabel.length) {
9086 //                Roo.log(" label");
9087                  cfg.cn = [
9088                    
9089                     {
9090                         tag: 'label',
9091                         //cls : 'input-group-addon',
9092                         html : this.fieldLabel
9093                         
9094                     },
9095                     
9096                     inputblock
9097                     
9098                 ];
9099
9100         } else {
9101             
9102 //                   Roo.log(" no label && no align");
9103                 cfg.cn = [
9104                     
9105                         inputblock
9106                     
9107                 ];
9108                 
9109                 
9110         }
9111         
9112         if (this.disabled) {
9113             input.disabled=true;
9114         }
9115         
9116         return cfg;
9117         
9118     },
9119     /**
9120      * return the real textarea element.
9121      */
9122     inputEl: function ()
9123     {
9124         return this.el.select('textarea.form-control',true).first();
9125     },
9126     
9127     /**
9128      * Clear any invalid styles/messages for this field
9129      */
9130     clearInvalid : function()
9131     {
9132         
9133         if(!this.el || this.preventMark){ // not rendered
9134             return;
9135         }
9136         
9137         var label = this.el.select('label', true).first();
9138         var icon = this.el.select('i.fa-star', true).first();
9139         
9140         if(label && icon){
9141             icon.remove();
9142         }
9143         
9144         this.el.removeClass(this.invalidClass);
9145         
9146         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9147             
9148             var feedback = this.el.select('.form-control-feedback', true).first();
9149             
9150             if(feedback){
9151                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9152             }
9153             
9154         }
9155         
9156         this.fireEvent('valid', this);
9157     },
9158     
9159      /**
9160      * Mark this field as valid
9161      */
9162     markValid : function()
9163     {
9164         if(!this.el  || this.preventMark){ // not rendered
9165             return;
9166         }
9167         
9168         this.el.removeClass([this.invalidClass, this.validClass]);
9169         
9170         var feedback = this.el.select('.form-control-feedback', true).first();
9171             
9172         if(feedback){
9173             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9174         }
9175
9176         if(this.disabled || this.allowBlank){
9177             return;
9178         }
9179         
9180         var label = this.el.select('label', true).first();
9181         var icon = this.el.select('i.fa-star', true).first();
9182         
9183         if(label && icon){
9184             icon.remove();
9185         }
9186         
9187         this.el.addClass(this.validClass);
9188         
9189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9190             
9191             var feedback = this.el.select('.form-control-feedback', true).first();
9192             
9193             if(feedback){
9194                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9195                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9196             }
9197             
9198         }
9199         
9200         this.fireEvent('valid', this);
9201     },
9202     
9203      /**
9204      * Mark this field as invalid
9205      * @param {String} msg The validation message
9206      */
9207     markInvalid : function(msg)
9208     {
9209         if(!this.el  || this.preventMark){ // not rendered
9210             return;
9211         }
9212         
9213         this.el.removeClass([this.invalidClass, this.validClass]);
9214         
9215         var feedback = this.el.select('.form-control-feedback', true).first();
9216             
9217         if(feedback){
9218             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9219         }
9220
9221         if(this.disabled || this.allowBlank){
9222             return;
9223         }
9224         
9225         var label = this.el.select('label', true).first();
9226         var icon = this.el.select('i.fa-star', true).first();
9227         
9228         if(!this.getValue().length && label && !icon){
9229             this.el.createChild({
9230                 tag : 'i',
9231                 cls : 'text-danger fa fa-lg fa-star',
9232                 tooltip : 'This field is required',
9233                 style : 'margin-right:5px;'
9234             }, label, true);
9235         }
9236
9237         this.el.addClass(this.invalidClass);
9238         
9239         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9240             
9241             var feedback = this.el.select('.form-control-feedback', true).first();
9242             
9243             if(feedback){
9244                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9245                 
9246                 if(this.getValue().length || this.forceFeedback){
9247                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9248                 }
9249                 
9250             }
9251             
9252         }
9253         
9254         this.fireEvent('invalid', this, msg);
9255     }
9256 });
9257
9258  
9259 /*
9260  * - LGPL
9261  *
9262  * trigger field - base class for combo..
9263  * 
9264  */
9265  
9266 /**
9267  * @class Roo.bootstrap.TriggerField
9268  * @extends Roo.bootstrap.Input
9269  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9270  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9271  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9272  * for which you can provide a custom implementation.  For example:
9273  * <pre><code>
9274 var trigger = new Roo.bootstrap.TriggerField();
9275 trigger.onTriggerClick = myTriggerFn;
9276 trigger.applyTo('my-field');
9277 </code></pre>
9278  *
9279  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9280  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9281  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9282  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9283  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9284
9285  * @constructor
9286  * Create a new TriggerField.
9287  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9288  * to the base TextField)
9289  */
9290 Roo.bootstrap.TriggerField = function(config){
9291     this.mimicing = false;
9292     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9293 };
9294
9295 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9296     /**
9297      * @cfg {String} triggerClass A CSS class to apply to the trigger
9298      */
9299      /**
9300      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9301      */
9302     hideTrigger:false,
9303
9304     /**
9305      * @cfg {Boolean} removable (true|false) special filter default false
9306      */
9307     removable : false,
9308     
9309     /** @cfg {Boolean} grow @hide */
9310     /** @cfg {Number} growMin @hide */
9311     /** @cfg {Number} growMax @hide */
9312
9313     /**
9314      * @hide 
9315      * @method
9316      */
9317     autoSize: Roo.emptyFn,
9318     // private
9319     monitorTab : true,
9320     // private
9321     deferHeight : true,
9322
9323     
9324     actionMode : 'wrap',
9325     
9326     caret : false,
9327     
9328     
9329     getAutoCreate : function(){
9330        
9331         var align = this.labelAlign || this.parentLabelAlign();
9332         
9333         var id = Roo.id();
9334         
9335         var cfg = {
9336             cls: 'form-group' //input-group
9337         };
9338         
9339         
9340         var input =  {
9341             tag: 'input',
9342             id : id,
9343             type : this.inputType,
9344             cls : 'form-control',
9345             autocomplete: 'new-password',
9346             placeholder : this.placeholder || '' 
9347             
9348         };
9349         if (this.name) {
9350             input.name = this.name;
9351         }
9352         if (this.size) {
9353             input.cls += ' input-' + this.size;
9354         }
9355         
9356         if (this.disabled) {
9357             input.disabled=true;
9358         }
9359         
9360         var inputblock = input;
9361         
9362         if(this.hasFeedback && !this.allowBlank){
9363             
9364             var feedback = {
9365                 tag: 'span',
9366                 cls: 'glyphicon form-control-feedback'
9367             };
9368             
9369             if(this.removable && !this.editable && !this.tickable){
9370                 inputblock = {
9371                     cls : 'has-feedback',
9372                     cn :  [
9373                         inputblock,
9374                         {
9375                             tag: 'button',
9376                             html : 'x',
9377                             cls : 'roo-combo-removable-btn close'
9378                         },
9379                         feedback
9380                     ] 
9381                 };
9382             } else {
9383                 inputblock = {
9384                     cls : 'has-feedback',
9385                     cn :  [
9386                         inputblock,
9387                         feedback
9388                     ] 
9389                 };
9390             }
9391
9392         } else {
9393             if(this.removable && !this.editable && !this.tickable){
9394                 inputblock = {
9395                     cls : 'roo-removable',
9396                     cn :  [
9397                         inputblock,
9398                         {
9399                             tag: 'button',
9400                             html : 'x',
9401                             cls : 'roo-combo-removable-btn close'
9402                         }
9403                     ] 
9404                 };
9405             }
9406         }
9407         
9408         if (this.before || this.after) {
9409             
9410             inputblock = {
9411                 cls : 'input-group',
9412                 cn :  [] 
9413             };
9414             if (this.before) {
9415                 inputblock.cn.push({
9416                     tag :'span',
9417                     cls : 'input-group-addon',
9418                     html : this.before
9419                 });
9420             }
9421             
9422             inputblock.cn.push(input);
9423             
9424             if(this.hasFeedback && !this.allowBlank){
9425                 inputblock.cls += ' has-feedback';
9426                 inputblock.cn.push(feedback);
9427             }
9428             
9429             if (this.after) {
9430                 inputblock.cn.push({
9431                     tag :'span',
9432                     cls : 'input-group-addon',
9433                     html : this.after
9434                 });
9435             }
9436             
9437         };
9438         
9439         var box = {
9440             tag: 'div',
9441             cn: [
9442                 {
9443                     tag: 'input',
9444                     type : 'hidden',
9445                     cls: 'form-hidden-field'
9446                 },
9447                 inputblock
9448             ]
9449             
9450         };
9451         
9452         if(this.multiple){
9453             box = {
9454                 tag: 'div',
9455                 cn: [
9456                     {
9457                         tag: 'input',
9458                         type : 'hidden',
9459                         cls: 'form-hidden-field'
9460                     },
9461                     {
9462                         tag: 'ul',
9463                         cls: 'roo-select2-choices',
9464                         cn:[
9465                             {
9466                                 tag: 'li',
9467                                 cls: 'roo-select2-search-field',
9468                                 cn: [
9469
9470                                     inputblock
9471                                 ]
9472                             }
9473                         ]
9474                     }
9475                 ]
9476             }
9477         };
9478         
9479         var combobox = {
9480             cls: 'roo-select2-container input-group',
9481             cn: [
9482                 box
9483 //                {
9484 //                    tag: 'ul',
9485 //                    cls: 'typeahead typeahead-long dropdown-menu',
9486 //                    style: 'display:none'
9487 //                }
9488             ]
9489         };
9490         
9491         if(!this.multiple && this.showToggleBtn){
9492             
9493             var caret = {
9494                         tag: 'span',
9495                         cls: 'caret'
9496              };
9497             if (this.caret != false) {
9498                 caret = {
9499                      tag: 'i',
9500                      cls: 'fa fa-' + this.caret
9501                 };
9502                 
9503             }
9504             
9505             combobox.cn.push({
9506                 tag :'span',
9507                 cls : 'input-group-addon btn dropdown-toggle',
9508                 cn : [
9509                     caret,
9510                     {
9511                         tag: 'span',
9512                         cls: 'combobox-clear',
9513                         cn  : [
9514                             {
9515                                 tag : 'i',
9516                                 cls: 'icon-remove'
9517                             }
9518                         ]
9519                     }
9520                 ]
9521
9522             })
9523         }
9524         
9525         if(this.multiple){
9526             combobox.cls += ' roo-select2-container-multi';
9527         }
9528         
9529         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9530             
9531 //                Roo.log("left and has label");
9532             cfg.cn = [
9533                 {
9534                     tag : 'i',
9535                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9536                     tooltip : 'This field is required'
9537                 },
9538                 {
9539                     tag: 'label',
9540                     'for' :  id,
9541                     cls : 'control-label col-sm-' + this.labelWidth,
9542                     html : this.fieldLabel
9543
9544                 },
9545                 {
9546                     cls : "col-sm-" + (12 - this.labelWidth), 
9547                     cn: [
9548                         combobox
9549                     ]
9550                 }
9551
9552             ];
9553             
9554             if(this.indicatorpos == 'right'){
9555                 cfg.cn = [
9556                     {
9557                         tag: 'label',
9558                         'for' :  id,
9559                         cls : 'control-label col-sm-' + this.labelWidth,
9560                         html : this.fieldLabel
9561
9562                     },
9563                     {
9564                         tag : 'i',
9565                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9566                         tooltip : 'This field is required'
9567                     },
9568                     {
9569                         cls : "col-sm-" + (12 - this.labelWidth), 
9570                         cn: [
9571                             combobox
9572                         ]
9573                     }
9574
9575                 ];
9576             }
9577             
9578         } else if ( this.fieldLabel.length) {
9579 //                Roo.log(" label");
9580             cfg.cn = [
9581                 {
9582                    tag : 'i',
9583                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9584                    tooltip : 'This field is required'
9585                },
9586                {
9587                    tag: 'label',
9588                    //cls : 'input-group-addon',
9589                    html : this.fieldLabel
9590
9591                },
9592
9593                combobox
9594
9595             ];
9596             
9597             if(this.indicatorpos == 'right'){
9598                 
9599                 cfg.cn = [
9600                     {
9601                        tag: 'label',
9602                        //cls : 'input-group-addon',
9603                        html : this.fieldLabel
9604
9605                     },
9606                     {
9607                        tag : 'i',
9608                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9609                        tooltip : 'This field is required'
9610                     },
9611                     
9612                     combobox
9613
9614                 ];
9615
9616             }
9617
9618         } else {
9619             
9620 //                Roo.log(" no label && no align");
9621                 cfg = combobox
9622                      
9623                 
9624         }
9625          
9626         var settings=this;
9627         ['xs','sm','md','lg'].map(function(size){
9628             if (settings[size]) {
9629                 cfg.cls += ' col-' + size + '-' + settings[size];
9630             }
9631         });
9632         
9633         return cfg;
9634         
9635     },
9636     
9637     
9638     
9639     // private
9640     onResize : function(w, h){
9641 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9642 //        if(typeof w == 'number'){
9643 //            var x = w - this.trigger.getWidth();
9644 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9645 //            this.trigger.setStyle('left', x+'px');
9646 //        }
9647     },
9648
9649     // private
9650     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9651
9652     // private
9653     getResizeEl : function(){
9654         return this.inputEl();
9655     },
9656
9657     // private
9658     getPositionEl : function(){
9659         return this.inputEl();
9660     },
9661
9662     // private
9663     alignErrorIcon : function(){
9664         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9665     },
9666
9667     // private
9668     initEvents : function(){
9669         
9670         this.createList();
9671         
9672         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9673         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9674         if(!this.multiple && this.showToggleBtn){
9675             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9676             if(this.hideTrigger){
9677                 this.trigger.setDisplayed(false);
9678             }
9679             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9680         }
9681         
9682         if(this.multiple){
9683             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9684         }
9685         
9686         if(this.removable && !this.editable && !this.tickable){
9687             var close = this.closeTriggerEl();
9688             
9689             if(close){
9690                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9691                 close.on('click', this.removeBtnClick, this, close);
9692             }
9693         }
9694         
9695         //this.trigger.addClassOnOver('x-form-trigger-over');
9696         //this.trigger.addClassOnClick('x-form-trigger-click');
9697         
9698         //if(!this.width){
9699         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9700         //}
9701     },
9702     
9703     closeTriggerEl : function()
9704     {
9705         var close = this.el.select('.roo-combo-removable-btn', true).first();
9706         return close ? close : false;
9707     },
9708     
9709     removeBtnClick : function(e, h, el)
9710     {
9711         e.preventDefault();
9712         
9713         if(this.fireEvent("remove", this) !== false){
9714             this.reset();
9715             this.fireEvent("afterremove", this)
9716         }
9717     },
9718     
9719     createList : function()
9720     {
9721         this.list = Roo.get(document.body).createChild({
9722             tag: 'ul',
9723             cls: 'typeahead typeahead-long dropdown-menu',
9724             style: 'display:none'
9725         });
9726         
9727         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9728         
9729     },
9730
9731     // private
9732     initTrigger : function(){
9733        
9734     },
9735
9736     // private
9737     onDestroy : function(){
9738         if(this.trigger){
9739             this.trigger.removeAllListeners();
9740           //  this.trigger.remove();
9741         }
9742         //if(this.wrap){
9743         //    this.wrap.remove();
9744         //}
9745         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9746     },
9747
9748     // private
9749     onFocus : function(){
9750         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9751         /*
9752         if(!this.mimicing){
9753             this.wrap.addClass('x-trigger-wrap-focus');
9754             this.mimicing = true;
9755             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9756             if(this.monitorTab){
9757                 this.el.on("keydown", this.checkTab, this);
9758             }
9759         }
9760         */
9761     },
9762
9763     // private
9764     checkTab : function(e){
9765         if(e.getKey() == e.TAB){
9766             this.triggerBlur();
9767         }
9768     },
9769
9770     // private
9771     onBlur : function(){
9772         // do nothing
9773     },
9774
9775     // private
9776     mimicBlur : function(e, t){
9777         /*
9778         if(!this.wrap.contains(t) && this.validateBlur()){
9779             this.triggerBlur();
9780         }
9781         */
9782     },
9783
9784     // private
9785     triggerBlur : function(){
9786         this.mimicing = false;
9787         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9788         if(this.monitorTab){
9789             this.el.un("keydown", this.checkTab, this);
9790         }
9791         //this.wrap.removeClass('x-trigger-wrap-focus');
9792         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9793     },
9794
9795     // private
9796     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9797     validateBlur : function(e, t){
9798         return true;
9799     },
9800
9801     // private
9802     onDisable : function(){
9803         this.inputEl().dom.disabled = true;
9804         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9805         //if(this.wrap){
9806         //    this.wrap.addClass('x-item-disabled');
9807         //}
9808     },
9809
9810     // private
9811     onEnable : function(){
9812         this.inputEl().dom.disabled = false;
9813         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9814         //if(this.wrap){
9815         //    this.el.removeClass('x-item-disabled');
9816         //}
9817     },
9818
9819     // private
9820     onShow : function(){
9821         var ae = this.getActionEl();
9822         
9823         if(ae){
9824             ae.dom.style.display = '';
9825             ae.dom.style.visibility = 'visible';
9826         }
9827     },
9828
9829     // private
9830     
9831     onHide : function(){
9832         var ae = this.getActionEl();
9833         ae.dom.style.display = 'none';
9834     },
9835
9836     /**
9837      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9838      * by an implementing function.
9839      * @method
9840      * @param {EventObject} e
9841      */
9842     onTriggerClick : Roo.emptyFn
9843 });
9844  /*
9845  * Based on:
9846  * Ext JS Library 1.1.1
9847  * Copyright(c) 2006-2007, Ext JS, LLC.
9848  *
9849  * Originally Released Under LGPL - original licence link has changed is not relivant.
9850  *
9851  * Fork - LGPL
9852  * <script type="text/javascript">
9853  */
9854
9855
9856 /**
9857  * @class Roo.data.SortTypes
9858  * @singleton
9859  * Defines the default sorting (casting?) comparison functions used when sorting data.
9860  */
9861 Roo.data.SortTypes = {
9862     /**
9863      * Default sort that does nothing
9864      * @param {Mixed} s The value being converted
9865      * @return {Mixed} The comparison value
9866      */
9867     none : function(s){
9868         return s;
9869     },
9870     
9871     /**
9872      * The regular expression used to strip tags
9873      * @type {RegExp}
9874      * @property
9875      */
9876     stripTagsRE : /<\/?[^>]+>/gi,
9877     
9878     /**
9879      * Strips all HTML tags to sort on text only
9880      * @param {Mixed} s The value being converted
9881      * @return {String} The comparison value
9882      */
9883     asText : function(s){
9884         return String(s).replace(this.stripTagsRE, "");
9885     },
9886     
9887     /**
9888      * Strips all HTML tags to sort on text only - Case insensitive
9889      * @param {Mixed} s The value being converted
9890      * @return {String} The comparison value
9891      */
9892     asUCText : function(s){
9893         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9894     },
9895     
9896     /**
9897      * Case insensitive string
9898      * @param {Mixed} s The value being converted
9899      * @return {String} The comparison value
9900      */
9901     asUCString : function(s) {
9902         return String(s).toUpperCase();
9903     },
9904     
9905     /**
9906      * Date sorting
9907      * @param {Mixed} s The value being converted
9908      * @return {Number} The comparison value
9909      */
9910     asDate : function(s) {
9911         if(!s){
9912             return 0;
9913         }
9914         if(s instanceof Date){
9915             return s.getTime();
9916         }
9917         return Date.parse(String(s));
9918     },
9919     
9920     /**
9921      * Float sorting
9922      * @param {Mixed} s The value being converted
9923      * @return {Float} The comparison value
9924      */
9925     asFloat : function(s) {
9926         var val = parseFloat(String(s).replace(/,/g, ""));
9927         if(isNaN(val)) {
9928             val = 0;
9929         }
9930         return val;
9931     },
9932     
9933     /**
9934      * Integer sorting
9935      * @param {Mixed} s The value being converted
9936      * @return {Number} The comparison value
9937      */
9938     asInt : function(s) {
9939         var val = parseInt(String(s).replace(/,/g, ""));
9940         if(isNaN(val)) {
9941             val = 0;
9942         }
9943         return val;
9944     }
9945 };/*
9946  * Based on:
9947  * Ext JS Library 1.1.1
9948  * Copyright(c) 2006-2007, Ext JS, LLC.
9949  *
9950  * Originally Released Under LGPL - original licence link has changed is not relivant.
9951  *
9952  * Fork - LGPL
9953  * <script type="text/javascript">
9954  */
9955
9956 /**
9957 * @class Roo.data.Record
9958  * Instances of this class encapsulate both record <em>definition</em> information, and record
9959  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9960  * to access Records cached in an {@link Roo.data.Store} object.<br>
9961  * <p>
9962  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9963  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9964  * objects.<br>
9965  * <p>
9966  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9967  * @constructor
9968  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9969  * {@link #create}. The parameters are the same.
9970  * @param {Array} data An associative Array of data values keyed by the field name.
9971  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9972  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9973  * not specified an integer id is generated.
9974  */
9975 Roo.data.Record = function(data, id){
9976     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9977     this.data = data;
9978 };
9979
9980 /**
9981  * Generate a constructor for a specific record layout.
9982  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9983  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9984  * Each field definition object may contain the following properties: <ul>
9985  * <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,
9986  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9987  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9988  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9989  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9990  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9991  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9992  * this may be omitted.</p></li>
9993  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9994  * <ul><li>auto (Default, implies no conversion)</li>
9995  * <li>string</li>
9996  * <li>int</li>
9997  * <li>float</li>
9998  * <li>boolean</li>
9999  * <li>date</li></ul></p></li>
10000  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10001  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10002  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10003  * by the Reader into an object that will be stored in the Record. It is passed the
10004  * following parameters:<ul>
10005  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10006  * </ul></p></li>
10007  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10008  * </ul>
10009  * <br>usage:<br><pre><code>
10010 var TopicRecord = Roo.data.Record.create(
10011     {name: 'title', mapping: 'topic_title'},
10012     {name: 'author', mapping: 'username'},
10013     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10014     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10015     {name: 'lastPoster', mapping: 'user2'},
10016     {name: 'excerpt', mapping: 'post_text'}
10017 );
10018
10019 var myNewRecord = new TopicRecord({
10020     title: 'Do my job please',
10021     author: 'noobie',
10022     totalPosts: 1,
10023     lastPost: new Date(),
10024     lastPoster: 'Animal',
10025     excerpt: 'No way dude!'
10026 });
10027 myStore.add(myNewRecord);
10028 </code></pre>
10029  * @method create
10030  * @static
10031  */
10032 Roo.data.Record.create = function(o){
10033     var f = function(){
10034         f.superclass.constructor.apply(this, arguments);
10035     };
10036     Roo.extend(f, Roo.data.Record);
10037     var p = f.prototype;
10038     p.fields = new Roo.util.MixedCollection(false, function(field){
10039         return field.name;
10040     });
10041     for(var i = 0, len = o.length; i < len; i++){
10042         p.fields.add(new Roo.data.Field(o[i]));
10043     }
10044     f.getField = function(name){
10045         return p.fields.get(name);  
10046     };
10047     return f;
10048 };
10049
10050 Roo.data.Record.AUTO_ID = 1000;
10051 Roo.data.Record.EDIT = 'edit';
10052 Roo.data.Record.REJECT = 'reject';
10053 Roo.data.Record.COMMIT = 'commit';
10054
10055 Roo.data.Record.prototype = {
10056     /**
10057      * Readonly flag - true if this record has been modified.
10058      * @type Boolean
10059      */
10060     dirty : false,
10061     editing : false,
10062     error: null,
10063     modified: null,
10064
10065     // private
10066     join : function(store){
10067         this.store = store;
10068     },
10069
10070     /**
10071      * Set the named field to the specified value.
10072      * @param {String} name The name of the field to set.
10073      * @param {Object} value The value to set the field to.
10074      */
10075     set : function(name, value){
10076         if(this.data[name] == value){
10077             return;
10078         }
10079         this.dirty = true;
10080         if(!this.modified){
10081             this.modified = {};
10082         }
10083         if(typeof this.modified[name] == 'undefined'){
10084             this.modified[name] = this.data[name];
10085         }
10086         this.data[name] = value;
10087         if(!this.editing && this.store){
10088             this.store.afterEdit(this);
10089         }       
10090     },
10091
10092     /**
10093      * Get the value of the named field.
10094      * @param {String} name The name of the field to get the value of.
10095      * @return {Object} The value of the field.
10096      */
10097     get : function(name){
10098         return this.data[name]; 
10099     },
10100
10101     // private
10102     beginEdit : function(){
10103         this.editing = true;
10104         this.modified = {}; 
10105     },
10106
10107     // private
10108     cancelEdit : function(){
10109         this.editing = false;
10110         delete this.modified;
10111     },
10112
10113     // private
10114     endEdit : function(){
10115         this.editing = false;
10116         if(this.dirty && this.store){
10117             this.store.afterEdit(this);
10118         }
10119     },
10120
10121     /**
10122      * Usually called by the {@link Roo.data.Store} which owns the Record.
10123      * Rejects all changes made to the Record since either creation, or the last commit operation.
10124      * Modified fields are reverted to their original values.
10125      * <p>
10126      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10127      * of reject operations.
10128      */
10129     reject : function(){
10130         var m = this.modified;
10131         for(var n in m){
10132             if(typeof m[n] != "function"){
10133                 this.data[n] = m[n];
10134             }
10135         }
10136         this.dirty = false;
10137         delete this.modified;
10138         this.editing = false;
10139         if(this.store){
10140             this.store.afterReject(this);
10141         }
10142     },
10143
10144     /**
10145      * Usually called by the {@link Roo.data.Store} which owns the Record.
10146      * Commits all changes made to the Record since either creation, or the last commit operation.
10147      * <p>
10148      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10149      * of commit operations.
10150      */
10151     commit : function(){
10152         this.dirty = false;
10153         delete this.modified;
10154         this.editing = false;
10155         if(this.store){
10156             this.store.afterCommit(this);
10157         }
10158     },
10159
10160     // private
10161     hasError : function(){
10162         return this.error != null;
10163     },
10164
10165     // private
10166     clearError : function(){
10167         this.error = null;
10168     },
10169
10170     /**
10171      * Creates a copy of this record.
10172      * @param {String} id (optional) A new record id if you don't want to use this record's id
10173      * @return {Record}
10174      */
10175     copy : function(newId) {
10176         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10177     }
10178 };/*
10179  * Based on:
10180  * Ext JS Library 1.1.1
10181  * Copyright(c) 2006-2007, Ext JS, LLC.
10182  *
10183  * Originally Released Under LGPL - original licence link has changed is not relivant.
10184  *
10185  * Fork - LGPL
10186  * <script type="text/javascript">
10187  */
10188
10189
10190
10191 /**
10192  * @class Roo.data.Store
10193  * @extends Roo.util.Observable
10194  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10195  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10196  * <p>
10197  * 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
10198  * has no knowledge of the format of the data returned by the Proxy.<br>
10199  * <p>
10200  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10201  * instances from the data object. These records are cached and made available through accessor functions.
10202  * @constructor
10203  * Creates a new Store.
10204  * @param {Object} config A config object containing the objects needed for the Store to access data,
10205  * and read the data into Records.
10206  */
10207 Roo.data.Store = function(config){
10208     this.data = new Roo.util.MixedCollection(false);
10209     this.data.getKey = function(o){
10210         return o.id;
10211     };
10212     this.baseParams = {};
10213     // private
10214     this.paramNames = {
10215         "start" : "start",
10216         "limit" : "limit",
10217         "sort" : "sort",
10218         "dir" : "dir",
10219         "multisort" : "_multisort"
10220     };
10221
10222     if(config && config.data){
10223         this.inlineData = config.data;
10224         delete config.data;
10225     }
10226
10227     Roo.apply(this, config);
10228     
10229     if(this.reader){ // reader passed
10230         this.reader = Roo.factory(this.reader, Roo.data);
10231         this.reader.xmodule = this.xmodule || false;
10232         if(!this.recordType){
10233             this.recordType = this.reader.recordType;
10234         }
10235         if(this.reader.onMetaChange){
10236             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10237         }
10238     }
10239
10240     if(this.recordType){
10241         this.fields = this.recordType.prototype.fields;
10242     }
10243     this.modified = [];
10244
10245     this.addEvents({
10246         /**
10247          * @event datachanged
10248          * Fires when the data cache has changed, and a widget which is using this Store
10249          * as a Record cache should refresh its view.
10250          * @param {Store} this
10251          */
10252         datachanged : true,
10253         /**
10254          * @event metachange
10255          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10256          * @param {Store} this
10257          * @param {Object} meta The JSON metadata
10258          */
10259         metachange : true,
10260         /**
10261          * @event add
10262          * Fires when Records have been added to the Store
10263          * @param {Store} this
10264          * @param {Roo.data.Record[]} records The array of Records added
10265          * @param {Number} index The index at which the record(s) were added
10266          */
10267         add : true,
10268         /**
10269          * @event remove
10270          * Fires when a Record has been removed from the Store
10271          * @param {Store} this
10272          * @param {Roo.data.Record} record The Record that was removed
10273          * @param {Number} index The index at which the record was removed
10274          */
10275         remove : true,
10276         /**
10277          * @event update
10278          * Fires when a Record has been updated
10279          * @param {Store} this
10280          * @param {Roo.data.Record} record The Record that was updated
10281          * @param {String} operation The update operation being performed.  Value may be one of:
10282          * <pre><code>
10283  Roo.data.Record.EDIT
10284  Roo.data.Record.REJECT
10285  Roo.data.Record.COMMIT
10286          * </code></pre>
10287          */
10288         update : true,
10289         /**
10290          * @event clear
10291          * Fires when the data cache has been cleared.
10292          * @param {Store} this
10293          */
10294         clear : true,
10295         /**
10296          * @event beforeload
10297          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10298          * the load action will be canceled.
10299          * @param {Store} this
10300          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10301          */
10302         beforeload : true,
10303         /**
10304          * @event beforeloadadd
10305          * Fires after a new set of Records has been loaded.
10306          * @param {Store} this
10307          * @param {Roo.data.Record[]} records The Records that were loaded
10308          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10309          */
10310         beforeloadadd : true,
10311         /**
10312          * @event load
10313          * Fires after a new set of Records has been loaded, before they are added to the store.
10314          * @param {Store} this
10315          * @param {Roo.data.Record[]} records The Records that were loaded
10316          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10317          * @params {Object} return from reader
10318          */
10319         load : true,
10320         /**
10321          * @event loadexception
10322          * Fires if an exception occurs in the Proxy during loading.
10323          * Called with the signature of the Proxy's "loadexception" event.
10324          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10325          * 
10326          * @param {Proxy} 
10327          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10328          * @param {Object} load options 
10329          * @param {Object} jsonData from your request (normally this contains the Exception)
10330          */
10331         loadexception : true
10332     });
10333     
10334     if(this.proxy){
10335         this.proxy = Roo.factory(this.proxy, Roo.data);
10336         this.proxy.xmodule = this.xmodule || false;
10337         this.relayEvents(this.proxy,  ["loadexception"]);
10338     }
10339     this.sortToggle = {};
10340     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10341
10342     Roo.data.Store.superclass.constructor.call(this);
10343
10344     if(this.inlineData){
10345         this.loadData(this.inlineData);
10346         delete this.inlineData;
10347     }
10348 };
10349
10350 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10351      /**
10352     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10353     * without a remote query - used by combo/forms at present.
10354     */
10355     
10356     /**
10357     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10358     */
10359     /**
10360     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10361     */
10362     /**
10363     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10364     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10365     */
10366     /**
10367     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10368     * on any HTTP request
10369     */
10370     /**
10371     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10372     */
10373     /**
10374     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10375     */
10376     multiSort: false,
10377     /**
10378     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10379     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10380     */
10381     remoteSort : false,
10382
10383     /**
10384     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10385      * loaded or when a record is removed. (defaults to false).
10386     */
10387     pruneModifiedRecords : false,
10388
10389     // private
10390     lastOptions : null,
10391
10392     /**
10393      * Add Records to the Store and fires the add event.
10394      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10395      */
10396     add : function(records){
10397         records = [].concat(records);
10398         for(var i = 0, len = records.length; i < len; i++){
10399             records[i].join(this);
10400         }
10401         var index = this.data.length;
10402         this.data.addAll(records);
10403         this.fireEvent("add", this, records, index);
10404     },
10405
10406     /**
10407      * Remove a Record from the Store and fires the remove event.
10408      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10409      */
10410     remove : function(record){
10411         var index = this.data.indexOf(record);
10412         this.data.removeAt(index);
10413         if(this.pruneModifiedRecords){
10414             this.modified.remove(record);
10415         }
10416         this.fireEvent("remove", this, record, index);
10417     },
10418
10419     /**
10420      * Remove all Records from the Store and fires the clear event.
10421      */
10422     removeAll : function(){
10423         this.data.clear();
10424         if(this.pruneModifiedRecords){
10425             this.modified = [];
10426         }
10427         this.fireEvent("clear", this);
10428     },
10429
10430     /**
10431      * Inserts Records to the Store at the given index and fires the add event.
10432      * @param {Number} index The start index at which to insert the passed Records.
10433      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10434      */
10435     insert : function(index, records){
10436         records = [].concat(records);
10437         for(var i = 0, len = records.length; i < len; i++){
10438             this.data.insert(index, records[i]);
10439             records[i].join(this);
10440         }
10441         this.fireEvent("add", this, records, index);
10442     },
10443
10444     /**
10445      * Get the index within the cache of the passed Record.
10446      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10447      * @return {Number} The index of the passed Record. Returns -1 if not found.
10448      */
10449     indexOf : function(record){
10450         return this.data.indexOf(record);
10451     },
10452
10453     /**
10454      * Get the index within the cache of the Record with the passed id.
10455      * @param {String} id The id of the Record to find.
10456      * @return {Number} The index of the Record. Returns -1 if not found.
10457      */
10458     indexOfId : function(id){
10459         return this.data.indexOfKey(id);
10460     },
10461
10462     /**
10463      * Get the Record with the specified id.
10464      * @param {String} id The id of the Record to find.
10465      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10466      */
10467     getById : function(id){
10468         return this.data.key(id);
10469     },
10470
10471     /**
10472      * Get the Record at the specified index.
10473      * @param {Number} index The index of the Record to find.
10474      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10475      */
10476     getAt : function(index){
10477         return this.data.itemAt(index);
10478     },
10479
10480     /**
10481      * Returns a range of Records between specified indices.
10482      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10483      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10484      * @return {Roo.data.Record[]} An array of Records
10485      */
10486     getRange : function(start, end){
10487         return this.data.getRange(start, end);
10488     },
10489
10490     // private
10491     storeOptions : function(o){
10492         o = Roo.apply({}, o);
10493         delete o.callback;
10494         delete o.scope;
10495         this.lastOptions = o;
10496     },
10497
10498     /**
10499      * Loads the Record cache from the configured Proxy using the configured Reader.
10500      * <p>
10501      * If using remote paging, then the first load call must specify the <em>start</em>
10502      * and <em>limit</em> properties in the options.params property to establish the initial
10503      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10504      * <p>
10505      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10506      * and this call will return before the new data has been loaded. Perform any post-processing
10507      * in a callback function, or in a "load" event handler.</strong>
10508      * <p>
10509      * @param {Object} options An object containing properties which control loading options:<ul>
10510      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10511      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10512      * passed the following arguments:<ul>
10513      * <li>r : Roo.data.Record[]</li>
10514      * <li>options: Options object from the load call</li>
10515      * <li>success: Boolean success indicator</li></ul></li>
10516      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10517      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10518      * </ul>
10519      */
10520     load : function(options){
10521         options = options || {};
10522         if(this.fireEvent("beforeload", this, options) !== false){
10523             this.storeOptions(options);
10524             var p = Roo.apply(options.params || {}, this.baseParams);
10525             // if meta was not loaded from remote source.. try requesting it.
10526             if (!this.reader.metaFromRemote) {
10527                 p._requestMeta = 1;
10528             }
10529             if(this.sortInfo && this.remoteSort){
10530                 var pn = this.paramNames;
10531                 p[pn["sort"]] = this.sortInfo.field;
10532                 p[pn["dir"]] = this.sortInfo.direction;
10533             }
10534             if (this.multiSort) {
10535                 var pn = this.paramNames;
10536                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10537             }
10538             
10539             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10540         }
10541     },
10542
10543     /**
10544      * Reloads the Record cache from the configured Proxy using the configured Reader and
10545      * the options from the last load operation performed.
10546      * @param {Object} options (optional) An object containing properties which may override the options
10547      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10548      * the most recently used options are reused).
10549      */
10550     reload : function(options){
10551         this.load(Roo.applyIf(options||{}, this.lastOptions));
10552     },
10553
10554     // private
10555     // Called as a callback by the Reader during a load operation.
10556     loadRecords : function(o, options, success){
10557         if(!o || success === false){
10558             if(success !== false){
10559                 this.fireEvent("load", this, [], options, o);
10560             }
10561             if(options.callback){
10562                 options.callback.call(options.scope || this, [], options, false);
10563             }
10564             return;
10565         }
10566         // if data returned failure - throw an exception.
10567         if (o.success === false) {
10568             // show a message if no listener is registered.
10569             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10570                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10571             }
10572             // loadmask wil be hooked into this..
10573             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10574             return;
10575         }
10576         var r = o.records, t = o.totalRecords || r.length;
10577         
10578         this.fireEvent("beforeloadadd", this, r, options, o);
10579         
10580         if(!options || options.add !== true){
10581             if(this.pruneModifiedRecords){
10582                 this.modified = [];
10583             }
10584             for(var i = 0, len = r.length; i < len; i++){
10585                 r[i].join(this);
10586             }
10587             if(this.snapshot){
10588                 this.data = this.snapshot;
10589                 delete this.snapshot;
10590             }
10591             this.data.clear();
10592             this.data.addAll(r);
10593             this.totalLength = t;
10594             this.applySort();
10595             this.fireEvent("datachanged", this);
10596         }else{
10597             this.totalLength = Math.max(t, this.data.length+r.length);
10598             this.add(r);
10599         }
10600         this.fireEvent("load", this, r, options, o);
10601         if(options.callback){
10602             options.callback.call(options.scope || this, r, options, true);
10603         }
10604     },
10605
10606
10607     /**
10608      * Loads data from a passed data block. A Reader which understands the format of the data
10609      * must have been configured in the constructor.
10610      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10611      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10612      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10613      */
10614     loadData : function(o, append){
10615         var r = this.reader.readRecords(o);
10616         this.loadRecords(r, {add: append}, true);
10617     },
10618
10619     /**
10620      * Gets the number of cached records.
10621      * <p>
10622      * <em>If using paging, this may not be the total size of the dataset. If the data object
10623      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10624      * the data set size</em>
10625      */
10626     getCount : function(){
10627         return this.data.length || 0;
10628     },
10629
10630     /**
10631      * Gets the total number of records in the dataset as returned by the server.
10632      * <p>
10633      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10634      * the dataset size</em>
10635      */
10636     getTotalCount : function(){
10637         return this.totalLength || 0;
10638     },
10639
10640     /**
10641      * Returns the sort state of the Store as an object with two properties:
10642      * <pre><code>
10643  field {String} The name of the field by which the Records are sorted
10644  direction {String} The sort order, "ASC" or "DESC"
10645      * </code></pre>
10646      */
10647     getSortState : function(){
10648         return this.sortInfo;
10649     },
10650
10651     // private
10652     applySort : function(){
10653         if(this.sortInfo && !this.remoteSort){
10654             var s = this.sortInfo, f = s.field;
10655             var st = this.fields.get(f).sortType;
10656             var fn = function(r1, r2){
10657                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10658                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10659             };
10660             this.data.sort(s.direction, fn);
10661             if(this.snapshot && this.snapshot != this.data){
10662                 this.snapshot.sort(s.direction, fn);
10663             }
10664         }
10665     },
10666
10667     /**
10668      * Sets the default sort column and order to be used by the next load operation.
10669      * @param {String} fieldName The name of the field to sort by.
10670      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10671      */
10672     setDefaultSort : function(field, dir){
10673         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10674     },
10675
10676     /**
10677      * Sort the Records.
10678      * If remote sorting is used, the sort is performed on the server, and the cache is
10679      * reloaded. If local sorting is used, the cache is sorted internally.
10680      * @param {String} fieldName The name of the field to sort by.
10681      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10682      */
10683     sort : function(fieldName, dir){
10684         var f = this.fields.get(fieldName);
10685         if(!dir){
10686             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10687             
10688             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10689                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10690             }else{
10691                 dir = f.sortDir;
10692             }
10693         }
10694         this.sortToggle[f.name] = dir;
10695         this.sortInfo = {field: f.name, direction: dir};
10696         if(!this.remoteSort){
10697             this.applySort();
10698             this.fireEvent("datachanged", this);
10699         }else{
10700             this.load(this.lastOptions);
10701         }
10702     },
10703
10704     /**
10705      * Calls the specified function for each of the Records in the cache.
10706      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10707      * Returning <em>false</em> aborts and exits the iteration.
10708      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10709      */
10710     each : function(fn, scope){
10711         this.data.each(fn, scope);
10712     },
10713
10714     /**
10715      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10716      * (e.g., during paging).
10717      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10718      */
10719     getModifiedRecords : function(){
10720         return this.modified;
10721     },
10722
10723     // private
10724     createFilterFn : function(property, value, anyMatch){
10725         if(!value.exec){ // not a regex
10726             value = String(value);
10727             if(value.length == 0){
10728                 return false;
10729             }
10730             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10731         }
10732         return function(r){
10733             return value.test(r.data[property]);
10734         };
10735     },
10736
10737     /**
10738      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10739      * @param {String} property A field on your records
10740      * @param {Number} start The record index to start at (defaults to 0)
10741      * @param {Number} end The last record index to include (defaults to length - 1)
10742      * @return {Number} The sum
10743      */
10744     sum : function(property, start, end){
10745         var rs = this.data.items, v = 0;
10746         start = start || 0;
10747         end = (end || end === 0) ? end : rs.length-1;
10748
10749         for(var i = start; i <= end; i++){
10750             v += (rs[i].data[property] || 0);
10751         }
10752         return v;
10753     },
10754
10755     /**
10756      * Filter the records by a specified property.
10757      * @param {String} field A field on your records
10758      * @param {String/RegExp} value Either a string that the field
10759      * should start with or a RegExp to test against the field
10760      * @param {Boolean} anyMatch True to match any part not just the beginning
10761      */
10762     filter : function(property, value, anyMatch){
10763         var fn = this.createFilterFn(property, value, anyMatch);
10764         return fn ? this.filterBy(fn) : this.clearFilter();
10765     },
10766
10767     /**
10768      * Filter by a function. The specified function will be called with each
10769      * record in this data source. If the function returns true the record is included,
10770      * otherwise it is filtered.
10771      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10772      * @param {Object} scope (optional) The scope of the function (defaults to this)
10773      */
10774     filterBy : function(fn, scope){
10775         this.snapshot = this.snapshot || this.data;
10776         this.data = this.queryBy(fn, scope||this);
10777         this.fireEvent("datachanged", this);
10778     },
10779
10780     /**
10781      * Query the records by a specified property.
10782      * @param {String} field A field on your records
10783      * @param {String/RegExp} value Either a string that the field
10784      * should start with or a RegExp to test against the field
10785      * @param {Boolean} anyMatch True to match any part not just the beginning
10786      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10787      */
10788     query : function(property, value, anyMatch){
10789         var fn = this.createFilterFn(property, value, anyMatch);
10790         return fn ? this.queryBy(fn) : this.data.clone();
10791     },
10792
10793     /**
10794      * Query by a function. The specified function will be called with each
10795      * record in this data source. If the function returns true the record is included
10796      * in the results.
10797      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10798      * @param {Object} scope (optional) The scope of the function (defaults to this)
10799       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10800      **/
10801     queryBy : function(fn, scope){
10802         var data = this.snapshot || this.data;
10803         return data.filterBy(fn, scope||this);
10804     },
10805
10806     /**
10807      * Collects unique values for a particular dataIndex from this store.
10808      * @param {String} dataIndex The property to collect
10809      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10810      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10811      * @return {Array} An array of the unique values
10812      **/
10813     collect : function(dataIndex, allowNull, bypassFilter){
10814         var d = (bypassFilter === true && this.snapshot) ?
10815                 this.snapshot.items : this.data.items;
10816         var v, sv, r = [], l = {};
10817         for(var i = 0, len = d.length; i < len; i++){
10818             v = d[i].data[dataIndex];
10819             sv = String(v);
10820             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10821                 l[sv] = true;
10822                 r[r.length] = v;
10823             }
10824         }
10825         return r;
10826     },
10827
10828     /**
10829      * Revert to a view of the Record cache with no filtering applied.
10830      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10831      */
10832     clearFilter : function(suppressEvent){
10833         if(this.snapshot && this.snapshot != this.data){
10834             this.data = this.snapshot;
10835             delete this.snapshot;
10836             if(suppressEvent !== true){
10837                 this.fireEvent("datachanged", this);
10838             }
10839         }
10840     },
10841
10842     // private
10843     afterEdit : function(record){
10844         if(this.modified.indexOf(record) == -1){
10845             this.modified.push(record);
10846         }
10847         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10848     },
10849     
10850     // private
10851     afterReject : function(record){
10852         this.modified.remove(record);
10853         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10854     },
10855
10856     // private
10857     afterCommit : function(record){
10858         this.modified.remove(record);
10859         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10860     },
10861
10862     /**
10863      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10864      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10865      */
10866     commitChanges : function(){
10867         var m = this.modified.slice(0);
10868         this.modified = [];
10869         for(var i = 0, len = m.length; i < len; i++){
10870             m[i].commit();
10871         }
10872     },
10873
10874     /**
10875      * Cancel outstanding changes on all changed records.
10876      */
10877     rejectChanges : function(){
10878         var m = this.modified.slice(0);
10879         this.modified = [];
10880         for(var i = 0, len = m.length; i < len; i++){
10881             m[i].reject();
10882         }
10883     },
10884
10885     onMetaChange : function(meta, rtype, o){
10886         this.recordType = rtype;
10887         this.fields = rtype.prototype.fields;
10888         delete this.snapshot;
10889         this.sortInfo = meta.sortInfo || this.sortInfo;
10890         this.modified = [];
10891         this.fireEvent('metachange', this, this.reader.meta);
10892     },
10893     
10894     moveIndex : function(data, type)
10895     {
10896         var index = this.indexOf(data);
10897         
10898         var newIndex = index + type;
10899         
10900         this.remove(data);
10901         
10902         this.insert(newIndex, data);
10903         
10904     }
10905 });/*
10906  * Based on:
10907  * Ext JS Library 1.1.1
10908  * Copyright(c) 2006-2007, Ext JS, LLC.
10909  *
10910  * Originally Released Under LGPL - original licence link has changed is not relivant.
10911  *
10912  * Fork - LGPL
10913  * <script type="text/javascript">
10914  */
10915
10916 /**
10917  * @class Roo.data.SimpleStore
10918  * @extends Roo.data.Store
10919  * Small helper class to make creating Stores from Array data easier.
10920  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10921  * @cfg {Array} fields An array of field definition objects, or field name strings.
10922  * @cfg {Array} data The multi-dimensional array of data
10923  * @constructor
10924  * @param {Object} config
10925  */
10926 Roo.data.SimpleStore = function(config){
10927     Roo.data.SimpleStore.superclass.constructor.call(this, {
10928         isLocal : true,
10929         reader: new Roo.data.ArrayReader({
10930                 id: config.id
10931             },
10932             Roo.data.Record.create(config.fields)
10933         ),
10934         proxy : new Roo.data.MemoryProxy(config.data)
10935     });
10936     this.load();
10937 };
10938 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10939  * Based on:
10940  * Ext JS Library 1.1.1
10941  * Copyright(c) 2006-2007, Ext JS, LLC.
10942  *
10943  * Originally Released Under LGPL - original licence link has changed is not relivant.
10944  *
10945  * Fork - LGPL
10946  * <script type="text/javascript">
10947  */
10948
10949 /**
10950 /**
10951  * @extends Roo.data.Store
10952  * @class Roo.data.JsonStore
10953  * Small helper class to make creating Stores for JSON data easier. <br/>
10954 <pre><code>
10955 var store = new Roo.data.JsonStore({
10956     url: 'get-images.php',
10957     root: 'images',
10958     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10959 });
10960 </code></pre>
10961  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10962  * JsonReader and HttpProxy (unless inline data is provided).</b>
10963  * @cfg {Array} fields An array of field definition objects, or field name strings.
10964  * @constructor
10965  * @param {Object} config
10966  */
10967 Roo.data.JsonStore = function(c){
10968     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10969         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10970         reader: new Roo.data.JsonReader(c, c.fields)
10971     }));
10972 };
10973 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10974  * Based on:
10975  * Ext JS Library 1.1.1
10976  * Copyright(c) 2006-2007, Ext JS, LLC.
10977  *
10978  * Originally Released Under LGPL - original licence link has changed is not relivant.
10979  *
10980  * Fork - LGPL
10981  * <script type="text/javascript">
10982  */
10983
10984  
10985 Roo.data.Field = function(config){
10986     if(typeof config == "string"){
10987         config = {name: config};
10988     }
10989     Roo.apply(this, config);
10990     
10991     if(!this.type){
10992         this.type = "auto";
10993     }
10994     
10995     var st = Roo.data.SortTypes;
10996     // named sortTypes are supported, here we look them up
10997     if(typeof this.sortType == "string"){
10998         this.sortType = st[this.sortType];
10999     }
11000     
11001     // set default sortType for strings and dates
11002     if(!this.sortType){
11003         switch(this.type){
11004             case "string":
11005                 this.sortType = st.asUCString;
11006                 break;
11007             case "date":
11008                 this.sortType = st.asDate;
11009                 break;
11010             default:
11011                 this.sortType = st.none;
11012         }
11013     }
11014
11015     // define once
11016     var stripRe = /[\$,%]/g;
11017
11018     // prebuilt conversion function for this field, instead of
11019     // switching every time we're reading a value
11020     if(!this.convert){
11021         var cv, dateFormat = this.dateFormat;
11022         switch(this.type){
11023             case "":
11024             case "auto":
11025             case undefined:
11026                 cv = function(v){ return v; };
11027                 break;
11028             case "string":
11029                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11030                 break;
11031             case "int":
11032                 cv = function(v){
11033                     return v !== undefined && v !== null && v !== '' ?
11034                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11035                     };
11036                 break;
11037             case "float":
11038                 cv = function(v){
11039                     return v !== undefined && v !== null && v !== '' ?
11040                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11041                     };
11042                 break;
11043             case "bool":
11044             case "boolean":
11045                 cv = function(v){ return v === true || v === "true" || v == 1; };
11046                 break;
11047             case "date":
11048                 cv = function(v){
11049                     if(!v){
11050                         return '';
11051                     }
11052                     if(v instanceof Date){
11053                         return v;
11054                     }
11055                     if(dateFormat){
11056                         if(dateFormat == "timestamp"){
11057                             return new Date(v*1000);
11058                         }
11059                         return Date.parseDate(v, dateFormat);
11060                     }
11061                     var parsed = Date.parse(v);
11062                     return parsed ? new Date(parsed) : null;
11063                 };
11064              break;
11065             
11066         }
11067         this.convert = cv;
11068     }
11069 };
11070
11071 Roo.data.Field.prototype = {
11072     dateFormat: null,
11073     defaultValue: "",
11074     mapping: null,
11075     sortType : null,
11076     sortDir : "ASC"
11077 };/*
11078  * Based on:
11079  * Ext JS Library 1.1.1
11080  * Copyright(c) 2006-2007, Ext JS, LLC.
11081  *
11082  * Originally Released Under LGPL - original licence link has changed is not relivant.
11083  *
11084  * Fork - LGPL
11085  * <script type="text/javascript">
11086  */
11087  
11088 // Base class for reading structured data from a data source.  This class is intended to be
11089 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11090
11091 /**
11092  * @class Roo.data.DataReader
11093  * Base class for reading structured data from a data source.  This class is intended to be
11094  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11095  */
11096
11097 Roo.data.DataReader = function(meta, recordType){
11098     
11099     this.meta = meta;
11100     
11101     this.recordType = recordType instanceof Array ? 
11102         Roo.data.Record.create(recordType) : recordType;
11103 };
11104
11105 Roo.data.DataReader.prototype = {
11106      /**
11107      * Create an empty record
11108      * @param {Object} data (optional) - overlay some values
11109      * @return {Roo.data.Record} record created.
11110      */
11111     newRow :  function(d) {
11112         var da =  {};
11113         this.recordType.prototype.fields.each(function(c) {
11114             switch( c.type) {
11115                 case 'int' : da[c.name] = 0; break;
11116                 case 'date' : da[c.name] = new Date(); break;
11117                 case 'float' : da[c.name] = 0.0; break;
11118                 case 'boolean' : da[c.name] = false; break;
11119                 default : da[c.name] = ""; break;
11120             }
11121             
11122         });
11123         return new this.recordType(Roo.apply(da, d));
11124     }
11125     
11126 };/*
11127  * Based on:
11128  * Ext JS Library 1.1.1
11129  * Copyright(c) 2006-2007, Ext JS, LLC.
11130  *
11131  * Originally Released Under LGPL - original licence link has changed is not relivant.
11132  *
11133  * Fork - LGPL
11134  * <script type="text/javascript">
11135  */
11136
11137 /**
11138  * @class Roo.data.DataProxy
11139  * @extends Roo.data.Observable
11140  * This class is an abstract base class for implementations which provide retrieval of
11141  * unformatted data objects.<br>
11142  * <p>
11143  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11144  * (of the appropriate type which knows how to parse the data object) to provide a block of
11145  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11146  * <p>
11147  * Custom implementations must implement the load method as described in
11148  * {@link Roo.data.HttpProxy#load}.
11149  */
11150 Roo.data.DataProxy = function(){
11151     this.addEvents({
11152         /**
11153          * @event beforeload
11154          * Fires before a network request is made to retrieve a data object.
11155          * @param {Object} This DataProxy object.
11156          * @param {Object} params The params parameter to the load function.
11157          */
11158         beforeload : true,
11159         /**
11160          * @event load
11161          * Fires before the load method's callback is called.
11162          * @param {Object} This DataProxy object.
11163          * @param {Object} o The data object.
11164          * @param {Object} arg The callback argument object passed to the load function.
11165          */
11166         load : true,
11167         /**
11168          * @event loadexception
11169          * Fires if an Exception occurs during data retrieval.
11170          * @param {Object} This DataProxy object.
11171          * @param {Object} o The data object.
11172          * @param {Object} arg The callback argument object passed to the load function.
11173          * @param {Object} e The Exception.
11174          */
11175         loadexception : true
11176     });
11177     Roo.data.DataProxy.superclass.constructor.call(this);
11178 };
11179
11180 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11181
11182     /**
11183      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11184      */
11185 /*
11186  * Based on:
11187  * Ext JS Library 1.1.1
11188  * Copyright(c) 2006-2007, Ext JS, LLC.
11189  *
11190  * Originally Released Under LGPL - original licence link has changed is not relivant.
11191  *
11192  * Fork - LGPL
11193  * <script type="text/javascript">
11194  */
11195 /**
11196  * @class Roo.data.MemoryProxy
11197  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11198  * to the Reader when its load method is called.
11199  * @constructor
11200  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11201  */
11202 Roo.data.MemoryProxy = function(data){
11203     if (data.data) {
11204         data = data.data;
11205     }
11206     Roo.data.MemoryProxy.superclass.constructor.call(this);
11207     this.data = data;
11208 };
11209
11210 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11211     
11212     /**
11213      * Load data from the requested source (in this case an in-memory
11214      * data object passed to the constructor), read the data object into
11215      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11216      * process that block using the passed callback.
11217      * @param {Object} params This parameter is not used by the MemoryProxy class.
11218      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11219      * object into a block of Roo.data.Records.
11220      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11221      * The function must be passed <ul>
11222      * <li>The Record block object</li>
11223      * <li>The "arg" argument from the load function</li>
11224      * <li>A boolean success indicator</li>
11225      * </ul>
11226      * @param {Object} scope The scope in which to call the callback
11227      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11228      */
11229     load : function(params, reader, callback, scope, arg){
11230         params = params || {};
11231         var result;
11232         try {
11233             result = reader.readRecords(this.data);
11234         }catch(e){
11235             this.fireEvent("loadexception", this, arg, null, e);
11236             callback.call(scope, null, arg, false);
11237             return;
11238         }
11239         callback.call(scope, result, arg, true);
11240     },
11241     
11242     // private
11243     update : function(params, records){
11244         
11245     }
11246 });/*
11247  * Based on:
11248  * Ext JS Library 1.1.1
11249  * Copyright(c) 2006-2007, Ext JS, LLC.
11250  *
11251  * Originally Released Under LGPL - original licence link has changed is not relivant.
11252  *
11253  * Fork - LGPL
11254  * <script type="text/javascript">
11255  */
11256 /**
11257  * @class Roo.data.HttpProxy
11258  * @extends Roo.data.DataProxy
11259  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11260  * configured to reference a certain URL.<br><br>
11261  * <p>
11262  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11263  * from which the running page was served.<br><br>
11264  * <p>
11265  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11266  * <p>
11267  * Be aware that to enable the browser to parse an XML document, the server must set
11268  * the Content-Type header in the HTTP response to "text/xml".
11269  * @constructor
11270  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11271  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11272  * will be used to make the request.
11273  */
11274 Roo.data.HttpProxy = function(conn){
11275     Roo.data.HttpProxy.superclass.constructor.call(this);
11276     // is conn a conn config or a real conn?
11277     this.conn = conn;
11278     this.useAjax = !conn || !conn.events;
11279   
11280 };
11281
11282 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11283     // thse are take from connection...
11284     
11285     /**
11286      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11287      */
11288     /**
11289      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11290      * extra parameters to each request made by this object. (defaults to undefined)
11291      */
11292     /**
11293      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11294      *  to each request made by this object. (defaults to undefined)
11295      */
11296     /**
11297      * @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)
11298      */
11299     /**
11300      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11301      */
11302      /**
11303      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11304      * @type Boolean
11305      */
11306   
11307
11308     /**
11309      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11310      * @type Boolean
11311      */
11312     /**
11313      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11314      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11315      * a finer-grained basis than the DataProxy events.
11316      */
11317     getConnection : function(){
11318         return this.useAjax ? Roo.Ajax : this.conn;
11319     },
11320
11321     /**
11322      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11323      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11324      * process that block using the passed callback.
11325      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11326      * for the request to the remote server.
11327      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11328      * object into a block of Roo.data.Records.
11329      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11330      * The function must be passed <ul>
11331      * <li>The Record block object</li>
11332      * <li>The "arg" argument from the load function</li>
11333      * <li>A boolean success indicator</li>
11334      * </ul>
11335      * @param {Object} scope The scope in which to call the callback
11336      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11337      */
11338     load : function(params, reader, callback, scope, arg){
11339         if(this.fireEvent("beforeload", this, params) !== false){
11340             var  o = {
11341                 params : params || {},
11342                 request: {
11343                     callback : callback,
11344                     scope : scope,
11345                     arg : arg
11346                 },
11347                 reader: reader,
11348                 callback : this.loadResponse,
11349                 scope: this
11350             };
11351             if(this.useAjax){
11352                 Roo.applyIf(o, this.conn);
11353                 if(this.activeRequest){
11354                     Roo.Ajax.abort(this.activeRequest);
11355                 }
11356                 this.activeRequest = Roo.Ajax.request(o);
11357             }else{
11358                 this.conn.request(o);
11359             }
11360         }else{
11361             callback.call(scope||this, null, arg, false);
11362         }
11363     },
11364
11365     // private
11366     loadResponse : function(o, success, response){
11367         delete this.activeRequest;
11368         if(!success){
11369             this.fireEvent("loadexception", this, o, response);
11370             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11371             return;
11372         }
11373         var result;
11374         try {
11375             result = o.reader.read(response);
11376         }catch(e){
11377             this.fireEvent("loadexception", this, o, response, e);
11378             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11379             return;
11380         }
11381         
11382         this.fireEvent("load", this, o, o.request.arg);
11383         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11384     },
11385
11386     // private
11387     update : function(dataSet){
11388
11389     },
11390
11391     // private
11392     updateResponse : function(dataSet){
11393
11394     }
11395 });/*
11396  * Based on:
11397  * Ext JS Library 1.1.1
11398  * Copyright(c) 2006-2007, Ext JS, LLC.
11399  *
11400  * Originally Released Under LGPL - original licence link has changed is not relivant.
11401  *
11402  * Fork - LGPL
11403  * <script type="text/javascript">
11404  */
11405
11406 /**
11407  * @class Roo.data.ScriptTagProxy
11408  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11409  * other than the originating domain of the running page.<br><br>
11410  * <p>
11411  * <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
11412  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11413  * <p>
11414  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11415  * source code that is used as the source inside a &lt;script> tag.<br><br>
11416  * <p>
11417  * In order for the browser to process the returned data, the server must wrap the data object
11418  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11419  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11420  * depending on whether the callback name was passed:
11421  * <p>
11422  * <pre><code>
11423 boolean scriptTag = false;
11424 String cb = request.getParameter("callback");
11425 if (cb != null) {
11426     scriptTag = true;
11427     response.setContentType("text/javascript");
11428 } else {
11429     response.setContentType("application/x-json");
11430 }
11431 Writer out = response.getWriter();
11432 if (scriptTag) {
11433     out.write(cb + "(");
11434 }
11435 out.print(dataBlock.toJsonString());
11436 if (scriptTag) {
11437     out.write(");");
11438 }
11439 </pre></code>
11440  *
11441  * @constructor
11442  * @param {Object} config A configuration object.
11443  */
11444 Roo.data.ScriptTagProxy = function(config){
11445     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11446     Roo.apply(this, config);
11447     this.head = document.getElementsByTagName("head")[0];
11448 };
11449
11450 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11451
11452 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11453     /**
11454      * @cfg {String} url The URL from which to request the data object.
11455      */
11456     /**
11457      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11458      */
11459     timeout : 30000,
11460     /**
11461      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11462      * the server the name of the callback function set up by the load call to process the returned data object.
11463      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11464      * javascript output which calls this named function passing the data object as its only parameter.
11465      */
11466     callbackParam : "callback",
11467     /**
11468      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11469      * name to the request.
11470      */
11471     nocache : true,
11472
11473     /**
11474      * Load data from the configured URL, read the data object into
11475      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11476      * process that block using the passed callback.
11477      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11478      * for the request to the remote server.
11479      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11480      * object into a block of Roo.data.Records.
11481      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11482      * The function must be passed <ul>
11483      * <li>The Record block object</li>
11484      * <li>The "arg" argument from the load function</li>
11485      * <li>A boolean success indicator</li>
11486      * </ul>
11487      * @param {Object} scope The scope in which to call the callback
11488      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11489      */
11490     load : function(params, reader, callback, scope, arg){
11491         if(this.fireEvent("beforeload", this, params) !== false){
11492
11493             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11494
11495             var url = this.url;
11496             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11497             if(this.nocache){
11498                 url += "&_dc=" + (new Date().getTime());
11499             }
11500             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11501             var trans = {
11502                 id : transId,
11503                 cb : "stcCallback"+transId,
11504                 scriptId : "stcScript"+transId,
11505                 params : params,
11506                 arg : arg,
11507                 url : url,
11508                 callback : callback,
11509                 scope : scope,
11510                 reader : reader
11511             };
11512             var conn = this;
11513
11514             window[trans.cb] = function(o){
11515                 conn.handleResponse(o, trans);
11516             };
11517
11518             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11519
11520             if(this.autoAbort !== false){
11521                 this.abort();
11522             }
11523
11524             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11525
11526             var script = document.createElement("script");
11527             script.setAttribute("src", url);
11528             script.setAttribute("type", "text/javascript");
11529             script.setAttribute("id", trans.scriptId);
11530             this.head.appendChild(script);
11531
11532             this.trans = trans;
11533         }else{
11534             callback.call(scope||this, null, arg, false);
11535         }
11536     },
11537
11538     // private
11539     isLoading : function(){
11540         return this.trans ? true : false;
11541     },
11542
11543     /**
11544      * Abort the current server request.
11545      */
11546     abort : function(){
11547         if(this.isLoading()){
11548             this.destroyTrans(this.trans);
11549         }
11550     },
11551
11552     // private
11553     destroyTrans : function(trans, isLoaded){
11554         this.head.removeChild(document.getElementById(trans.scriptId));
11555         clearTimeout(trans.timeoutId);
11556         if(isLoaded){
11557             window[trans.cb] = undefined;
11558             try{
11559                 delete window[trans.cb];
11560             }catch(e){}
11561         }else{
11562             // if hasn't been loaded, wait for load to remove it to prevent script error
11563             window[trans.cb] = function(){
11564                 window[trans.cb] = undefined;
11565                 try{
11566                     delete window[trans.cb];
11567                 }catch(e){}
11568             };
11569         }
11570     },
11571
11572     // private
11573     handleResponse : function(o, trans){
11574         this.trans = false;
11575         this.destroyTrans(trans, true);
11576         var result;
11577         try {
11578             result = trans.reader.readRecords(o);
11579         }catch(e){
11580             this.fireEvent("loadexception", this, o, trans.arg, e);
11581             trans.callback.call(trans.scope||window, null, trans.arg, false);
11582             return;
11583         }
11584         this.fireEvent("load", this, o, trans.arg);
11585         trans.callback.call(trans.scope||window, result, trans.arg, true);
11586     },
11587
11588     // private
11589     handleFailure : function(trans){
11590         this.trans = false;
11591         this.destroyTrans(trans, false);
11592         this.fireEvent("loadexception", this, null, trans.arg);
11593         trans.callback.call(trans.scope||window, null, trans.arg, false);
11594     }
11595 });/*
11596  * Based on:
11597  * Ext JS Library 1.1.1
11598  * Copyright(c) 2006-2007, Ext JS, LLC.
11599  *
11600  * Originally Released Under LGPL - original licence link has changed is not relivant.
11601  *
11602  * Fork - LGPL
11603  * <script type="text/javascript">
11604  */
11605
11606 /**
11607  * @class Roo.data.JsonReader
11608  * @extends Roo.data.DataReader
11609  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11610  * based on mappings in a provided Roo.data.Record constructor.
11611  * 
11612  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11613  * in the reply previously. 
11614  * 
11615  * <p>
11616  * Example code:
11617  * <pre><code>
11618 var RecordDef = Roo.data.Record.create([
11619     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11620     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11621 ]);
11622 var myReader = new Roo.data.JsonReader({
11623     totalProperty: "results",    // The property which contains the total dataset size (optional)
11624     root: "rows",                // The property which contains an Array of row objects
11625     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11626 }, RecordDef);
11627 </code></pre>
11628  * <p>
11629  * This would consume a JSON file like this:
11630  * <pre><code>
11631 { 'results': 2, 'rows': [
11632     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11633     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11634 }
11635 </code></pre>
11636  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11637  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11638  * paged from the remote server.
11639  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11640  * @cfg {String} root name of the property which contains the Array of row objects.
11641  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11642  * @cfg {Array} fields Array of field definition objects
11643  * @constructor
11644  * Create a new JsonReader
11645  * @param {Object} meta Metadata configuration options
11646  * @param {Object} recordType Either an Array of field definition objects,
11647  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11648  */
11649 Roo.data.JsonReader = function(meta, recordType){
11650     
11651     meta = meta || {};
11652     // set some defaults:
11653     Roo.applyIf(meta, {
11654         totalProperty: 'total',
11655         successProperty : 'success',
11656         root : 'data',
11657         id : 'id'
11658     });
11659     
11660     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11661 };
11662 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11663     
11664     /**
11665      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11666      * Used by Store query builder to append _requestMeta to params.
11667      * 
11668      */
11669     metaFromRemote : false,
11670     /**
11671      * This method is only used by a DataProxy which has retrieved data from a remote server.
11672      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11673      * @return {Object} data A data block which is used by an Roo.data.Store object as
11674      * a cache of Roo.data.Records.
11675      */
11676     read : function(response){
11677         var json = response.responseText;
11678        
11679         var o = /* eval:var:o */ eval("("+json+")");
11680         if(!o) {
11681             throw {message: "JsonReader.read: Json object not found"};
11682         }
11683         
11684         if(o.metaData){
11685             
11686             delete this.ef;
11687             this.metaFromRemote = true;
11688             this.meta = o.metaData;
11689             this.recordType = Roo.data.Record.create(o.metaData.fields);
11690             this.onMetaChange(this.meta, this.recordType, o);
11691         }
11692         return this.readRecords(o);
11693     },
11694
11695     // private function a store will implement
11696     onMetaChange : function(meta, recordType, o){
11697
11698     },
11699
11700     /**
11701          * @ignore
11702          */
11703     simpleAccess: function(obj, subsc) {
11704         return obj[subsc];
11705     },
11706
11707         /**
11708          * @ignore
11709          */
11710     getJsonAccessor: function(){
11711         var re = /[\[\.]/;
11712         return function(expr) {
11713             try {
11714                 return(re.test(expr))
11715                     ? new Function("obj", "return obj." + expr)
11716                     : function(obj){
11717                         return obj[expr];
11718                     };
11719             } catch(e){}
11720             return Roo.emptyFn;
11721         };
11722     }(),
11723
11724     /**
11725      * Create a data block containing Roo.data.Records from an XML document.
11726      * @param {Object} o An object which contains an Array of row objects in the property specified
11727      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11728      * which contains the total size of the dataset.
11729      * @return {Object} data A data block which is used by an Roo.data.Store object as
11730      * a cache of Roo.data.Records.
11731      */
11732     readRecords : function(o){
11733         /**
11734          * After any data loads, the raw JSON data is available for further custom processing.
11735          * @type Object
11736          */
11737         this.o = o;
11738         var s = this.meta, Record = this.recordType,
11739             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11740
11741 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11742         if (!this.ef) {
11743             if(s.totalProperty) {
11744                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11745                 }
11746                 if(s.successProperty) {
11747                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11748                 }
11749                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11750                 if (s.id) {
11751                         var g = this.getJsonAccessor(s.id);
11752                         this.getId = function(rec) {
11753                                 var r = g(rec);  
11754                                 return (r === undefined || r === "") ? null : r;
11755                         };
11756                 } else {
11757                         this.getId = function(){return null;};
11758                 }
11759             this.ef = [];
11760             for(var jj = 0; jj < fl; jj++){
11761                 f = fi[jj];
11762                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11763                 this.ef[jj] = this.getJsonAccessor(map);
11764             }
11765         }
11766
11767         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11768         if(s.totalProperty){
11769             var vt = parseInt(this.getTotal(o), 10);
11770             if(!isNaN(vt)){
11771                 totalRecords = vt;
11772             }
11773         }
11774         if(s.successProperty){
11775             var vs = this.getSuccess(o);
11776             if(vs === false || vs === 'false'){
11777                 success = false;
11778             }
11779         }
11780         var records = [];
11781         for(var i = 0; i < c; i++){
11782                 var n = root[i];
11783             var values = {};
11784             var id = this.getId(n);
11785             for(var j = 0; j < fl; j++){
11786                 f = fi[j];
11787             var v = this.ef[j](n);
11788             if (!f.convert) {
11789                 Roo.log('missing convert for ' + f.name);
11790                 Roo.log(f);
11791                 continue;
11792             }
11793             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11794             }
11795             var record = new Record(values, id);
11796             record.json = n;
11797             records[i] = record;
11798         }
11799         return {
11800             raw : o,
11801             success : success,
11802             records : records,
11803             totalRecords : totalRecords
11804         };
11805     }
11806 });/*
11807  * Based on:
11808  * Ext JS Library 1.1.1
11809  * Copyright(c) 2006-2007, Ext JS, LLC.
11810  *
11811  * Originally Released Under LGPL - original licence link has changed is not relivant.
11812  *
11813  * Fork - LGPL
11814  * <script type="text/javascript">
11815  */
11816
11817 /**
11818  * @class Roo.data.ArrayReader
11819  * @extends Roo.data.DataReader
11820  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11821  * Each element of that Array represents a row of data fields. The
11822  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11823  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11824  * <p>
11825  * Example code:.
11826  * <pre><code>
11827 var RecordDef = Roo.data.Record.create([
11828     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11829     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11830 ]);
11831 var myReader = new Roo.data.ArrayReader({
11832     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11833 }, RecordDef);
11834 </code></pre>
11835  * <p>
11836  * This would consume an Array like this:
11837  * <pre><code>
11838 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11839   </code></pre>
11840  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11841  * @constructor
11842  * Create a new JsonReader
11843  * @param {Object} meta Metadata configuration options.
11844  * @param {Object} recordType Either an Array of field definition objects
11845  * as specified to {@link Roo.data.Record#create},
11846  * or an {@link Roo.data.Record} object
11847  * created using {@link Roo.data.Record#create}.
11848  */
11849 Roo.data.ArrayReader = function(meta, recordType){
11850     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11851 };
11852
11853 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11854     /**
11855      * Create a data block containing Roo.data.Records from an XML document.
11856      * @param {Object} o An Array of row objects which represents the dataset.
11857      * @return {Object} data A data block which is used by an Roo.data.Store object as
11858      * a cache of Roo.data.Records.
11859      */
11860     readRecords : function(o){
11861         var sid = this.meta ? this.meta.id : null;
11862         var recordType = this.recordType, fields = recordType.prototype.fields;
11863         var records = [];
11864         var root = o;
11865             for(var i = 0; i < root.length; i++){
11866                     var n = root[i];
11867                 var values = {};
11868                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11869                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11870                 var f = fields.items[j];
11871                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11872                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11873                 v = f.convert(v);
11874                 values[f.name] = v;
11875             }
11876                 var record = new recordType(values, id);
11877                 record.json = n;
11878                 records[records.length] = record;
11879             }
11880             return {
11881                 records : records,
11882                 totalRecords : records.length
11883             };
11884     }
11885 });/*
11886  * - LGPL
11887  * * 
11888  */
11889
11890 /**
11891  * @class Roo.bootstrap.ComboBox
11892  * @extends Roo.bootstrap.TriggerField
11893  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11894  * @cfg {Boolean} append (true|false) default false
11895  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11896  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11897  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11898  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11899  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11900  * @cfg {Boolean} animate default true
11901  * @cfg {Boolean} emptyResultText only for touch device
11902  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11903  * @constructor
11904  * Create a new ComboBox.
11905  * @param {Object} config Configuration options
11906  */
11907 Roo.bootstrap.ComboBox = function(config){
11908     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11909     this.addEvents({
11910         /**
11911          * @event expand
11912          * Fires when the dropdown list is expanded
11913              * @param {Roo.bootstrap.ComboBox} combo This combo box
11914              */
11915         'expand' : true,
11916         /**
11917          * @event collapse
11918          * Fires when the dropdown list is collapsed
11919              * @param {Roo.bootstrap.ComboBox} combo This combo box
11920              */
11921         'collapse' : true,
11922         /**
11923          * @event beforeselect
11924          * Fires before a list item is selected. Return false to cancel the selection.
11925              * @param {Roo.bootstrap.ComboBox} combo This combo box
11926              * @param {Roo.data.Record} record The data record returned from the underlying store
11927              * @param {Number} index The index of the selected item in the dropdown list
11928              */
11929         'beforeselect' : true,
11930         /**
11931          * @event select
11932          * Fires when a list item is selected
11933              * @param {Roo.bootstrap.ComboBox} combo This combo box
11934              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11935              * @param {Number} index The index of the selected item in the dropdown list
11936              */
11937         'select' : true,
11938         /**
11939          * @event beforequery
11940          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11941          * The event object passed has these properties:
11942              * @param {Roo.bootstrap.ComboBox} combo This combo box
11943              * @param {String} query The query
11944              * @param {Boolean} forceAll true to force "all" query
11945              * @param {Boolean} cancel true to cancel the query
11946              * @param {Object} e The query event object
11947              */
11948         'beforequery': true,
11949          /**
11950          * @event add
11951          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11952              * @param {Roo.bootstrap.ComboBox} combo This combo box
11953              */
11954         'add' : true,
11955         /**
11956          * @event edit
11957          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11958              * @param {Roo.bootstrap.ComboBox} combo This combo box
11959              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11960              */
11961         'edit' : true,
11962         /**
11963          * @event remove
11964          * Fires when the remove value from the combobox array
11965              * @param {Roo.bootstrap.ComboBox} combo This combo box
11966              */
11967         'remove' : true,
11968         /**
11969          * @event afterremove
11970          * Fires when the remove value from the combobox array
11971              * @param {Roo.bootstrap.ComboBox} combo This combo box
11972              */
11973         'afterremove' : true,
11974         /**
11975          * @event specialfilter
11976          * Fires when specialfilter
11977             * @param {Roo.bootstrap.ComboBox} combo This combo box
11978             */
11979         'specialfilter' : true,
11980         /**
11981          * @event tick
11982          * Fires when tick the element
11983             * @param {Roo.bootstrap.ComboBox} combo This combo box
11984             */
11985         'tick' : true,
11986         /**
11987          * @event touchviewdisplay
11988          * Fires when touch view require special display (default is using displayField)
11989             * @param {Roo.bootstrap.ComboBox} combo This combo box
11990             * @param {Object} cfg set html .
11991             */
11992         'touchviewdisplay' : true
11993         
11994     });
11995     
11996     this.item = [];
11997     this.tickItems = [];
11998     
11999     this.selectedIndex = -1;
12000     if(this.mode == 'local'){
12001         if(config.queryDelay === undefined){
12002             this.queryDelay = 10;
12003         }
12004         if(config.minChars === undefined){
12005             this.minChars = 0;
12006         }
12007     }
12008 };
12009
12010 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12011      
12012     /**
12013      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12014      * rendering into an Roo.Editor, defaults to false)
12015      */
12016     /**
12017      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12018      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12019      */
12020     /**
12021      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12022      */
12023     /**
12024      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12025      * the dropdown list (defaults to undefined, with no header element)
12026      */
12027
12028      /**
12029      * @cfg {String/Roo.Template} tpl The template to use to render the output
12030      */
12031      
12032      /**
12033      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12034      */
12035     listWidth: undefined,
12036     /**
12037      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12038      * mode = 'remote' or 'text' if mode = 'local')
12039      */
12040     displayField: undefined,
12041     
12042     /**
12043      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12044      * mode = 'remote' or 'value' if mode = 'local'). 
12045      * Note: use of a valueField requires the user make a selection
12046      * in order for a value to be mapped.
12047      */
12048     valueField: undefined,
12049     /**
12050      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12051      */
12052     modalTitle : '',
12053     
12054     /**
12055      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12056      * field's data value (defaults to the underlying DOM element's name)
12057      */
12058     hiddenName: undefined,
12059     /**
12060      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12061      */
12062     listClass: '',
12063     /**
12064      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12065      */
12066     selectedClass: 'active',
12067     
12068     /**
12069      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12070      */
12071     shadow:'sides',
12072     /**
12073      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12074      * anchor positions (defaults to 'tl-bl')
12075      */
12076     listAlign: 'tl-bl?',
12077     /**
12078      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12079      */
12080     maxHeight: 300,
12081     /**
12082      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12083      * query specified by the allQuery config option (defaults to 'query')
12084      */
12085     triggerAction: 'query',
12086     /**
12087      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12088      * (defaults to 4, does not apply if editable = false)
12089      */
12090     minChars : 4,
12091     /**
12092      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12093      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12094      */
12095     typeAhead: false,
12096     /**
12097      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12098      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12099      */
12100     queryDelay: 500,
12101     /**
12102      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12103      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12104      */
12105     pageSize: 0,
12106     /**
12107      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12108      * when editable = true (defaults to false)
12109      */
12110     selectOnFocus:false,
12111     /**
12112      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12113      */
12114     queryParam: 'query',
12115     /**
12116      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12117      * when mode = 'remote' (defaults to 'Loading...')
12118      */
12119     loadingText: 'Loading...',
12120     /**
12121      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12122      */
12123     resizable: false,
12124     /**
12125      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12126      */
12127     handleHeight : 8,
12128     /**
12129      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12130      * traditional select (defaults to true)
12131      */
12132     editable: true,
12133     /**
12134      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12135      */
12136     allQuery: '',
12137     /**
12138      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12139      */
12140     mode: 'remote',
12141     /**
12142      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12143      * listWidth has a higher value)
12144      */
12145     minListWidth : 70,
12146     /**
12147      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12148      * allow the user to set arbitrary text into the field (defaults to false)
12149      */
12150     forceSelection:false,
12151     /**
12152      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12153      * if typeAhead = true (defaults to 250)
12154      */
12155     typeAheadDelay : 250,
12156     /**
12157      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12158      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12159      */
12160     valueNotFoundText : undefined,
12161     /**
12162      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12163      */
12164     blockFocus : false,
12165     
12166     /**
12167      * @cfg {Boolean} disableClear Disable showing of clear button.
12168      */
12169     disableClear : false,
12170     /**
12171      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12172      */
12173     alwaysQuery : false,
12174     
12175     /**
12176      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12177      */
12178     multiple : false,
12179     
12180     /**
12181      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12182      */
12183     invalidClass : "has-warning",
12184     
12185     /**
12186      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12187      */
12188     validClass : "has-success",
12189     
12190     /**
12191      * @cfg {Boolean} specialFilter (true|false) special filter default false
12192      */
12193     specialFilter : false,
12194     
12195     /**
12196      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12197      */
12198     mobileTouchView : true,
12199     
12200     //private
12201     addicon : false,
12202     editicon: false,
12203     
12204     page: 0,
12205     hasQuery: false,
12206     append: false,
12207     loadNext: false,
12208     autoFocus : true,
12209     tickable : false,
12210     btnPosition : 'right',
12211     triggerList : true,
12212     showToggleBtn : true,
12213     animate : true,
12214     emptyResultText: 'Empty',
12215     triggerText : 'Select',
12216     
12217     // element that contains real text value.. (when hidden is used..)
12218     
12219     getAutoCreate : function()
12220     {
12221         var cfg = false;
12222         
12223         /*
12224          * Touch Devices
12225          */
12226         
12227         if(Roo.isTouch && this.mobileTouchView){
12228             cfg = this.getAutoCreateTouchView();
12229             return cfg;;
12230         }
12231         
12232         /*
12233          *  Normal ComboBox
12234          */
12235         if(!this.tickable){
12236             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12237             return cfg;
12238         }
12239         
12240         /*
12241          *  ComboBox with tickable selections
12242          */
12243              
12244         var align = this.labelAlign || this.parentLabelAlign();
12245         
12246         cfg = {
12247             cls : 'form-group roo-combobox-tickable' //input-group
12248         };
12249         
12250         var buttons = {
12251             tag : 'div',
12252             cls : 'tickable-buttons',
12253             cn : [
12254                 {
12255                     tag : 'button',
12256                     type : 'button',
12257                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12258                     html : this.triggerText
12259                 },
12260                 {
12261                     tag : 'button',
12262                     type : 'button',
12263                     name : 'ok',
12264                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12265                     html : 'Done'
12266                 },
12267                 {
12268                     tag : 'button',
12269                     type : 'button',
12270                     name : 'cancel',
12271                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12272                     html : 'Cancel'
12273                 }
12274             ]
12275         };
12276         
12277         if(this.editable){
12278             buttons.cn.unshift({
12279                 tag: 'input',
12280                 cls: 'roo-select2-search-field-input'
12281             });
12282         }
12283         
12284         var _this = this;
12285         
12286         Roo.each(buttons.cn, function(c){
12287             if (_this.size) {
12288                 c.cls += ' btn-' + _this.size;
12289             }
12290
12291             if (_this.disabled) {
12292                 c.disabled = true;
12293             }
12294         });
12295         
12296         var box = {
12297             tag: 'div',
12298             cn: [
12299                 {
12300                     tag: 'input',
12301                     type : 'hidden',
12302                     cls: 'form-hidden-field'
12303                 },
12304                 {
12305                     tag: 'ul',
12306                     cls: 'roo-select2-choices',
12307                     cn:[
12308                         {
12309                             tag: 'li',
12310                             cls: 'roo-select2-search-field',
12311                             cn: [
12312
12313                                 buttons
12314                             ]
12315                         }
12316                     ]
12317                 }
12318             ]
12319         };
12320         
12321         var combobox = {
12322             cls: 'roo-select2-container input-group roo-select2-container-multi',
12323             cn: [
12324                 box
12325 //                {
12326 //                    tag: 'ul',
12327 //                    cls: 'typeahead typeahead-long dropdown-menu',
12328 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12329 //                }
12330             ]
12331         };
12332         
12333         if(this.hasFeedback && !this.allowBlank){
12334             
12335             var feedback = {
12336                 tag: 'span',
12337                 cls: 'glyphicon form-control-feedback'
12338             };
12339
12340             combobox.cn.push(feedback);
12341         }
12342         
12343         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12344             
12345 //                Roo.log("left and has label");
12346             cfg.cn = [
12347                 {
12348                     tag : 'i',
12349                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12350                     tooltip : 'This field is required'
12351                 },
12352                 {
12353                     tag: 'label',
12354                     'for' :  id,
12355                     cls : 'control-label col-sm-' + this.labelWidth,
12356                     html : this.fieldLabel
12357
12358                 },
12359                 {
12360                     cls : "col-sm-" + (12 - this.labelWidth), 
12361                     cn: [
12362                         combobox
12363                     ]
12364                 }
12365
12366             ];
12367
12368             if(this.indicatorpos == 'right'){
12369                 
12370                 cfg.cn = [
12371                     {
12372                         tag: 'label',
12373                         'for' :  id,
12374                         cls : 'control-label col-sm-' + this.labelWidth,
12375                         html : this.fieldLabel
12376
12377                     },
12378                     {
12379                         tag : 'i',
12380                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12381                         tooltip : 'This field is required'
12382                     },
12383                     {
12384                         cls : "col-sm-" + (12 - this.labelWidth), 
12385                         cn: [
12386                             combobox
12387                         ]
12388                     }
12389
12390                 ];
12391             
12392             }
12393                 
12394                 
12395         } else if ( this.fieldLabel.length) {
12396 //                Roo.log(" label");
12397                  cfg.cn = [
12398                     {
12399                         tag : 'i',
12400                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12401                         tooltip : 'This field is required'
12402                     },
12403                     {
12404                         tag: 'label',
12405                         //cls : 'input-group-addon',
12406                         html : this.fieldLabel
12407                         
12408                     },
12409                     
12410                     combobox
12411                     
12412                 ];
12413                 
12414                 if(this.indicatorpos == 'right'){
12415                     
12416                     cfg.cn = [
12417                         {
12418                             tag: 'label',
12419                             //cls : 'input-group-addon',
12420                             html : this.fieldLabel
12421
12422                         },
12423                         
12424                         {
12425                             tag : 'i',
12426                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12427                             tooltip : 'This field is required'
12428                         },
12429                         
12430                         combobox
12431
12432                     ];
12433                 
12434                 }
12435
12436         } else {
12437             
12438 //                Roo.log(" no label && no align");
12439                 cfg = combobox
12440                      
12441                 
12442         }
12443          
12444         var settings=this;
12445         ['xs','sm','md','lg'].map(function(size){
12446             if (settings[size]) {
12447                 cfg.cls += ' col-' + size + '-' + settings[size];
12448             }
12449         });
12450         
12451         return cfg;
12452         
12453     },
12454     
12455     _initEventsCalled : false,
12456     
12457     // private
12458     initEvents: function()
12459     {
12460         
12461         if (this._initEventsCalled) { // as we call render... prevent looping...
12462             return;
12463         }
12464         this._initEventsCalled = true;
12465         
12466         if (!this.store) {
12467             throw "can not find store for combo";
12468         }
12469         
12470         this.store = Roo.factory(this.store, Roo.data);
12471         
12472         // if we are building from html. then this element is so complex, that we can not really
12473         // use the rendered HTML.
12474         // so we have to trash and replace the previous code.
12475         if (Roo.XComponent.build_from_html) {
12476             
12477             // remove this element....
12478             var e = this.el.dom, k=0;
12479             while (e ) { e = e.previousSibling;  ++k;}
12480
12481             this.el.remove();
12482             
12483             this.el=false;
12484             this.rendered = false;
12485             
12486             this.render(this.parent().getChildContainer(true), k);
12487             
12488             
12489             
12490         }
12491         
12492         
12493         /*
12494          * Touch Devices
12495          */
12496         
12497         if(Roo.isTouch && this.mobileTouchView){
12498             this.initTouchView();
12499             return;
12500         }
12501         
12502         if(this.tickable){
12503             this.initTickableEvents();
12504             return;
12505         }
12506         
12507         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12508         
12509         if(this.hiddenName){
12510             
12511             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12512             
12513             this.hiddenField.dom.value =
12514                 this.hiddenValue !== undefined ? this.hiddenValue :
12515                 this.value !== undefined ? this.value : '';
12516
12517             // prevent input submission
12518             this.el.dom.removeAttribute('name');
12519             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12520              
12521              
12522         }
12523         //if(Roo.isGecko){
12524         //    this.el.dom.setAttribute('autocomplete', 'off');
12525         //}
12526         
12527         var cls = 'x-combo-list';
12528         
12529         //this.list = new Roo.Layer({
12530         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12531         //});
12532         
12533         var _this = this;
12534         
12535         (function(){
12536             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12537             _this.list.setWidth(lw);
12538         }).defer(100);
12539         
12540         this.list.on('mouseover', this.onViewOver, this);
12541         this.list.on('mousemove', this.onViewMove, this);
12542         
12543         this.list.on('scroll', this.onViewScroll, this);
12544         
12545         /*
12546         this.list.swallowEvent('mousewheel');
12547         this.assetHeight = 0;
12548
12549         if(this.title){
12550             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12551             this.assetHeight += this.header.getHeight();
12552         }
12553
12554         this.innerList = this.list.createChild({cls:cls+'-inner'});
12555         this.innerList.on('mouseover', this.onViewOver, this);
12556         this.innerList.on('mousemove', this.onViewMove, this);
12557         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12558         
12559         if(this.allowBlank && !this.pageSize && !this.disableClear){
12560             this.footer = this.list.createChild({cls:cls+'-ft'});
12561             this.pageTb = new Roo.Toolbar(this.footer);
12562            
12563         }
12564         if(this.pageSize){
12565             this.footer = this.list.createChild({cls:cls+'-ft'});
12566             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12567                     {pageSize: this.pageSize});
12568             
12569         }
12570         
12571         if (this.pageTb && this.allowBlank && !this.disableClear) {
12572             var _this = this;
12573             this.pageTb.add(new Roo.Toolbar.Fill(), {
12574                 cls: 'x-btn-icon x-btn-clear',
12575                 text: '&#160;',
12576                 handler: function()
12577                 {
12578                     _this.collapse();
12579                     _this.clearValue();
12580                     _this.onSelect(false, -1);
12581                 }
12582             });
12583         }
12584         if (this.footer) {
12585             this.assetHeight += this.footer.getHeight();
12586         }
12587         */
12588             
12589         if(!this.tpl){
12590             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12591         }
12592
12593         this.view = new Roo.View(this.list, this.tpl, {
12594             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12595         });
12596         //this.view.wrapEl.setDisplayed(false);
12597         this.view.on('click', this.onViewClick, this);
12598         
12599         
12600         
12601         this.store.on('beforeload', this.onBeforeLoad, this);
12602         this.store.on('load', this.onLoad, this);
12603         this.store.on('loadexception', this.onLoadException, this);
12604         /*
12605         if(this.resizable){
12606             this.resizer = new Roo.Resizable(this.list,  {
12607                pinned:true, handles:'se'
12608             });
12609             this.resizer.on('resize', function(r, w, h){
12610                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12611                 this.listWidth = w;
12612                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12613                 this.restrictHeight();
12614             }, this);
12615             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12616         }
12617         */
12618         if(!this.editable){
12619             this.editable = true;
12620             this.setEditable(false);
12621         }
12622         
12623         /*
12624         
12625         if (typeof(this.events.add.listeners) != 'undefined') {
12626             
12627             this.addicon = this.wrap.createChild(
12628                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12629        
12630             this.addicon.on('click', function(e) {
12631                 this.fireEvent('add', this);
12632             }, this);
12633         }
12634         if (typeof(this.events.edit.listeners) != 'undefined') {
12635             
12636             this.editicon = this.wrap.createChild(
12637                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12638             if (this.addicon) {
12639                 this.editicon.setStyle('margin-left', '40px');
12640             }
12641             this.editicon.on('click', function(e) {
12642                 
12643                 // we fire even  if inothing is selected..
12644                 this.fireEvent('edit', this, this.lastData );
12645                 
12646             }, this);
12647         }
12648         */
12649         
12650         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12651             "up" : function(e){
12652                 this.inKeyMode = true;
12653                 this.selectPrev();
12654             },
12655
12656             "down" : function(e){
12657                 if(!this.isExpanded()){
12658                     this.onTriggerClick();
12659                 }else{
12660                     this.inKeyMode = true;
12661                     this.selectNext();
12662                 }
12663             },
12664
12665             "enter" : function(e){
12666 //                this.onViewClick();
12667                 //return true;
12668                 this.collapse();
12669                 
12670                 if(this.fireEvent("specialkey", this, e)){
12671                     this.onViewClick(false);
12672                 }
12673                 
12674                 return true;
12675             },
12676
12677             "esc" : function(e){
12678                 this.collapse();
12679             },
12680
12681             "tab" : function(e){
12682                 this.collapse();
12683                 
12684                 if(this.fireEvent("specialkey", this, e)){
12685                     this.onViewClick(false);
12686                 }
12687                 
12688                 return true;
12689             },
12690
12691             scope : this,
12692
12693             doRelay : function(foo, bar, hname){
12694                 if(hname == 'down' || this.scope.isExpanded()){
12695                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12696                 }
12697                 return true;
12698             },
12699
12700             forceKeyDown: true
12701         });
12702         
12703         
12704         this.queryDelay = Math.max(this.queryDelay || 10,
12705                 this.mode == 'local' ? 10 : 250);
12706         
12707         
12708         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12709         
12710         if(this.typeAhead){
12711             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12712         }
12713         if(this.editable !== false){
12714             this.inputEl().on("keyup", this.onKeyUp, this);
12715         }
12716         if(this.forceSelection){
12717             this.inputEl().on('blur', this.doForce, this);
12718         }
12719         
12720         if(this.multiple){
12721             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12722             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12723         }
12724     },
12725     
12726     initTickableEvents: function()
12727     {   
12728         this.createList();
12729         
12730         if(this.hiddenName){
12731             
12732             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12733             
12734             this.hiddenField.dom.value =
12735                 this.hiddenValue !== undefined ? this.hiddenValue :
12736                 this.value !== undefined ? this.value : '';
12737
12738             // prevent input submission
12739             this.el.dom.removeAttribute('name');
12740             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12741              
12742              
12743         }
12744         
12745 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12746         
12747         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12748         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12749         if(this.triggerList){
12750             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12751         }
12752          
12753         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12754         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12755         
12756         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12757         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12758         
12759         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12760         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12761         
12762         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12763         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12764         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12765         
12766         this.okBtn.hide();
12767         this.cancelBtn.hide();
12768         
12769         var _this = this;
12770         
12771         (function(){
12772             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12773             _this.list.setWidth(lw);
12774         }).defer(100);
12775         
12776         this.list.on('mouseover', this.onViewOver, this);
12777         this.list.on('mousemove', this.onViewMove, this);
12778         
12779         this.list.on('scroll', this.onViewScroll, this);
12780         
12781         if(!this.tpl){
12782             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>';
12783         }
12784
12785         this.view = new Roo.View(this.list, this.tpl, {
12786             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12787         });
12788         
12789         //this.view.wrapEl.setDisplayed(false);
12790         this.view.on('click', this.onViewClick, this);
12791         
12792         
12793         
12794         this.store.on('beforeload', this.onBeforeLoad, this);
12795         this.store.on('load', this.onLoad, this);
12796         this.store.on('loadexception', this.onLoadException, this);
12797         
12798         if(this.editable){
12799             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12800                 "up" : function(e){
12801                     this.inKeyMode = true;
12802                     this.selectPrev();
12803                 },
12804
12805                 "down" : function(e){
12806                     this.inKeyMode = true;
12807                     this.selectNext();
12808                 },
12809
12810                 "enter" : function(e){
12811                     if(this.fireEvent("specialkey", this, e)){
12812                         this.onViewClick(false);
12813                     }
12814                     
12815                     return true;
12816                 },
12817
12818                 "esc" : function(e){
12819                     this.onTickableFooterButtonClick(e, false, false);
12820                 },
12821
12822                 "tab" : function(e){
12823                     this.fireEvent("specialkey", this, e);
12824                     
12825                     this.onTickableFooterButtonClick(e, false, false);
12826                     
12827                     return true;
12828                 },
12829
12830                 scope : this,
12831
12832                 doRelay : function(e, fn, key){
12833                     if(this.scope.isExpanded()){
12834                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12835                     }
12836                     return true;
12837                 },
12838
12839                 forceKeyDown: true
12840             });
12841         }
12842         
12843         this.queryDelay = Math.max(this.queryDelay || 10,
12844                 this.mode == 'local' ? 10 : 250);
12845         
12846         
12847         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12848         
12849         if(this.typeAhead){
12850             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12851         }
12852         
12853         if(this.editable !== false){
12854             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12855         }
12856         
12857     },
12858
12859     onDestroy : function(){
12860         if(this.view){
12861             this.view.setStore(null);
12862             this.view.el.removeAllListeners();
12863             this.view.el.remove();
12864             this.view.purgeListeners();
12865         }
12866         if(this.list){
12867             this.list.dom.innerHTML  = '';
12868         }
12869         
12870         if(this.store){
12871             this.store.un('beforeload', this.onBeforeLoad, this);
12872             this.store.un('load', this.onLoad, this);
12873             this.store.un('loadexception', this.onLoadException, this);
12874         }
12875         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12876     },
12877
12878     // private
12879     fireKey : function(e){
12880         if(e.isNavKeyPress() && !this.list.isVisible()){
12881             this.fireEvent("specialkey", this, e);
12882         }
12883     },
12884
12885     // private
12886     onResize: function(w, h){
12887 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12888 //        
12889 //        if(typeof w != 'number'){
12890 //            // we do not handle it!?!?
12891 //            return;
12892 //        }
12893 //        var tw = this.trigger.getWidth();
12894 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12895 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12896 //        var x = w - tw;
12897 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12898 //            
12899 //        //this.trigger.setStyle('left', x+'px');
12900 //        
12901 //        if(this.list && this.listWidth === undefined){
12902 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12903 //            this.list.setWidth(lw);
12904 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12905 //        }
12906         
12907     
12908         
12909     },
12910
12911     /**
12912      * Allow or prevent the user from directly editing the field text.  If false is passed,
12913      * the user will only be able to select from the items defined in the dropdown list.  This method
12914      * is the runtime equivalent of setting the 'editable' config option at config time.
12915      * @param {Boolean} value True to allow the user to directly edit the field text
12916      */
12917     setEditable : function(value){
12918         if(value == this.editable){
12919             return;
12920         }
12921         this.editable = value;
12922         if(!value){
12923             this.inputEl().dom.setAttribute('readOnly', true);
12924             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12925             this.inputEl().addClass('x-combo-noedit');
12926         }else{
12927             this.inputEl().dom.setAttribute('readOnly', false);
12928             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12929             this.inputEl().removeClass('x-combo-noedit');
12930         }
12931     },
12932
12933     // private
12934     
12935     onBeforeLoad : function(combo,opts){
12936         if(!this.hasFocus){
12937             return;
12938         }
12939          if (!opts.add) {
12940             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12941          }
12942         this.restrictHeight();
12943         this.selectedIndex = -1;
12944     },
12945
12946     // private
12947     onLoad : function(){
12948         
12949         this.hasQuery = false;
12950         
12951         if(!this.hasFocus){
12952             return;
12953         }
12954         
12955         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12956             this.loading.hide();
12957         }
12958              
12959         if(this.store.getCount() > 0){
12960             this.expand();
12961             this.restrictHeight();
12962             if(this.lastQuery == this.allQuery){
12963                 if(this.editable && !this.tickable){
12964                     this.inputEl().dom.select();
12965                 }
12966                 
12967                 if(
12968                     !this.selectByValue(this.value, true) &&
12969                     this.autoFocus && 
12970                     (
12971                         !this.store.lastOptions ||
12972                         typeof(this.store.lastOptions.add) == 'undefined' || 
12973                         this.store.lastOptions.add != true
12974                     )
12975                 ){
12976                     this.select(0, true);
12977                 }
12978             }else{
12979                 if(this.autoFocus){
12980                     this.selectNext();
12981                 }
12982                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12983                     this.taTask.delay(this.typeAheadDelay);
12984                 }
12985             }
12986         }else{
12987             this.onEmptyResults();
12988         }
12989         
12990         //this.el.focus();
12991     },
12992     // private
12993     onLoadException : function()
12994     {
12995         this.hasQuery = false;
12996         
12997         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12998             this.loading.hide();
12999         }
13000         
13001         if(this.tickable && this.editable){
13002             return;
13003         }
13004         
13005         this.collapse();
13006         // only causes errors at present
13007         //Roo.log(this.store.reader.jsonData);
13008         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13009             // fixme
13010             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13011         //}
13012         
13013         
13014     },
13015     // private
13016     onTypeAhead : function(){
13017         if(this.store.getCount() > 0){
13018             var r = this.store.getAt(0);
13019             var newValue = r.data[this.displayField];
13020             var len = newValue.length;
13021             var selStart = this.getRawValue().length;
13022             
13023             if(selStart != len){
13024                 this.setRawValue(newValue);
13025                 this.selectText(selStart, newValue.length);
13026             }
13027         }
13028     },
13029
13030     // private
13031     onSelect : function(record, index){
13032         
13033         if(this.fireEvent('beforeselect', this, record, index) !== false){
13034         
13035             this.setFromData(index > -1 ? record.data : false);
13036             
13037             this.collapse();
13038             this.fireEvent('select', this, record, index);
13039         }
13040     },
13041
13042     /**
13043      * Returns the currently selected field value or empty string if no value is set.
13044      * @return {String} value The selected value
13045      */
13046     getValue : function(){
13047         
13048         if(this.multiple){
13049             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13050         }
13051         
13052         if(this.valueField){
13053             return typeof this.value != 'undefined' ? this.value : '';
13054         }else{
13055             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13056         }
13057     },
13058
13059     /**
13060      * Clears any text/value currently set in the field
13061      */
13062     clearValue : function(){
13063         if(this.hiddenField){
13064             this.hiddenField.dom.value = '';
13065         }
13066         this.value = '';
13067         this.setRawValue('');
13068         this.lastSelectionText = '';
13069         this.lastData = false;
13070         
13071         var close = this.closeTriggerEl();
13072         
13073         if(close){
13074             close.hide();
13075         }
13076         
13077         this.validate();
13078         
13079     },
13080
13081     /**
13082      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13083      * will be displayed in the field.  If the value does not match the data value of an existing item,
13084      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13085      * Otherwise the field will be blank (although the value will still be set).
13086      * @param {String} value The value to match
13087      */
13088     setValue : function(v){
13089         if(this.multiple){
13090             this.syncValue();
13091             return;
13092         }
13093         
13094         var text = v;
13095         if(this.valueField){
13096             var r = this.findRecord(this.valueField, v);
13097             if(r){
13098                 text = r.data[this.displayField];
13099             }else if(this.valueNotFoundText !== undefined){
13100                 text = this.valueNotFoundText;
13101             }
13102         }
13103         this.lastSelectionText = text;
13104         if(this.hiddenField){
13105             this.hiddenField.dom.value = v;
13106         }
13107         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13108         this.value = v;
13109         
13110         var close = this.closeTriggerEl();
13111         
13112         if(close){
13113             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13114         }
13115         
13116         this.validate();
13117     },
13118     /**
13119      * @property {Object} the last set data for the element
13120      */
13121     
13122     lastData : false,
13123     /**
13124      * Sets the value of the field based on a object which is related to the record format for the store.
13125      * @param {Object} value the value to set as. or false on reset?
13126      */
13127     setFromData : function(o){
13128         
13129         if(this.multiple){
13130             this.addItem(o);
13131             return;
13132         }
13133             
13134         var dv = ''; // display value
13135         var vv = ''; // value value..
13136         this.lastData = o;
13137         if (this.displayField) {
13138             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13139         } else {
13140             // this is an error condition!!!
13141             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13142         }
13143         
13144         if(this.valueField){
13145             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13146         }
13147         
13148         var close = this.closeTriggerEl();
13149         
13150         if(close){
13151             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13152         }
13153         
13154         if(this.hiddenField){
13155             this.hiddenField.dom.value = vv;
13156             
13157             this.lastSelectionText = dv;
13158             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13159             this.value = vv;
13160             return;
13161         }
13162         // no hidden field.. - we store the value in 'value', but still display
13163         // display field!!!!
13164         this.lastSelectionText = dv;
13165         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13166         this.value = vv;
13167         
13168         
13169         
13170     },
13171     // private
13172     reset : function(){
13173         // overridden so that last data is reset..
13174         
13175         if(this.multiple){
13176             this.clearItem();
13177             return;
13178         }
13179         
13180         this.setValue(this.originalValue);
13181         //this.clearInvalid();
13182         this.lastData = false;
13183         if (this.view) {
13184             this.view.clearSelections();
13185         }
13186         
13187         this.validate();
13188     },
13189     // private
13190     findRecord : function(prop, value){
13191         var record;
13192         if(this.store.getCount() > 0){
13193             this.store.each(function(r){
13194                 if(r.data[prop] == value){
13195                     record = r;
13196                     return false;
13197                 }
13198                 return true;
13199             });
13200         }
13201         return record;
13202     },
13203     
13204     getName: function()
13205     {
13206         // returns hidden if it's set..
13207         if (!this.rendered) {return ''};
13208         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13209         
13210     },
13211     // private
13212     onViewMove : function(e, t){
13213         this.inKeyMode = false;
13214     },
13215
13216     // private
13217     onViewOver : function(e, t){
13218         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13219             return;
13220         }
13221         var item = this.view.findItemFromChild(t);
13222         
13223         if(item){
13224             var index = this.view.indexOf(item);
13225             this.select(index, false);
13226         }
13227     },
13228
13229     // private
13230     onViewClick : function(view, doFocus, el, e)
13231     {
13232         var index = this.view.getSelectedIndexes()[0];
13233         
13234         var r = this.store.getAt(index);
13235         
13236         if(this.tickable){
13237             
13238             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13239                 return;
13240             }
13241             
13242             var rm = false;
13243             var _this = this;
13244             
13245             Roo.each(this.tickItems, function(v,k){
13246                 
13247                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13248                     Roo.log(v);
13249                     _this.tickItems.splice(k, 1);
13250                     
13251                     if(typeof(e) == 'undefined' && view == false){
13252                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13253                     }
13254                     
13255                     rm = true;
13256                     return;
13257                 }
13258             });
13259             
13260             if(rm){
13261                 return;
13262             }
13263             
13264             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13265                 this.tickItems.push(r.data);
13266             }
13267             
13268             if(typeof(e) == 'undefined' && view == false){
13269                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13270             }
13271                     
13272             return;
13273         }
13274         
13275         if(r){
13276             this.onSelect(r, index);
13277         }
13278         if(doFocus !== false && !this.blockFocus){
13279             this.inputEl().focus();
13280         }
13281     },
13282
13283     // private
13284     restrictHeight : function(){
13285         //this.innerList.dom.style.height = '';
13286         //var inner = this.innerList.dom;
13287         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13288         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13289         //this.list.beginUpdate();
13290         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13291         this.list.alignTo(this.inputEl(), this.listAlign);
13292         this.list.alignTo(this.inputEl(), this.listAlign);
13293         //this.list.endUpdate();
13294     },
13295
13296     // private
13297     onEmptyResults : function(){
13298         
13299         if(this.tickable && this.editable){
13300             this.restrictHeight();
13301             return;
13302         }
13303         
13304         this.collapse();
13305     },
13306
13307     /**
13308      * Returns true if the dropdown list is expanded, else false.
13309      */
13310     isExpanded : function(){
13311         return this.list.isVisible();
13312     },
13313
13314     /**
13315      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13316      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13317      * @param {String} value The data value of the item to select
13318      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13319      * selected item if it is not currently in view (defaults to true)
13320      * @return {Boolean} True if the value matched an item in the list, else false
13321      */
13322     selectByValue : function(v, scrollIntoView){
13323         if(v !== undefined && v !== null){
13324             var r = this.findRecord(this.valueField || this.displayField, v);
13325             if(r){
13326                 this.select(this.store.indexOf(r), scrollIntoView);
13327                 return true;
13328             }
13329         }
13330         return false;
13331     },
13332
13333     /**
13334      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13335      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13336      * @param {Number} index The zero-based index of the list item to select
13337      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13338      * selected item if it is not currently in view (defaults to true)
13339      */
13340     select : function(index, scrollIntoView){
13341         this.selectedIndex = index;
13342         this.view.select(index);
13343         if(scrollIntoView !== false){
13344             var el = this.view.getNode(index);
13345             /*
13346              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13347              */
13348             if(el){
13349                 this.list.scrollChildIntoView(el, false);
13350             }
13351         }
13352     },
13353
13354     // private
13355     selectNext : function(){
13356         var ct = this.store.getCount();
13357         if(ct > 0){
13358             if(this.selectedIndex == -1){
13359                 this.select(0);
13360             }else if(this.selectedIndex < ct-1){
13361                 this.select(this.selectedIndex+1);
13362             }
13363         }
13364     },
13365
13366     // private
13367     selectPrev : function(){
13368         var ct = this.store.getCount();
13369         if(ct > 0){
13370             if(this.selectedIndex == -1){
13371                 this.select(0);
13372             }else if(this.selectedIndex != 0){
13373                 this.select(this.selectedIndex-1);
13374             }
13375         }
13376     },
13377
13378     // private
13379     onKeyUp : function(e){
13380         if(this.editable !== false && !e.isSpecialKey()){
13381             this.lastKey = e.getKey();
13382             this.dqTask.delay(this.queryDelay);
13383         }
13384     },
13385
13386     // private
13387     validateBlur : function(){
13388         return !this.list || !this.list.isVisible();   
13389     },
13390
13391     // private
13392     initQuery : function(){
13393         
13394         var v = this.getRawValue();
13395         
13396         if(this.tickable && this.editable){
13397             v = this.tickableInputEl().getValue();
13398         }
13399         
13400         this.doQuery(v);
13401     },
13402
13403     // private
13404     doForce : function(){
13405         if(this.inputEl().dom.value.length > 0){
13406             this.inputEl().dom.value =
13407                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13408              
13409         }
13410     },
13411
13412     /**
13413      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13414      * query allowing the query action to be canceled if needed.
13415      * @param {String} query The SQL query to execute
13416      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13417      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13418      * saved in the current store (defaults to false)
13419      */
13420     doQuery : function(q, forceAll){
13421         
13422         if(q === undefined || q === null){
13423             q = '';
13424         }
13425         var qe = {
13426             query: q,
13427             forceAll: forceAll,
13428             combo: this,
13429             cancel:false
13430         };
13431         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13432             return false;
13433         }
13434         q = qe.query;
13435         
13436         forceAll = qe.forceAll;
13437         if(forceAll === true || (q.length >= this.minChars)){
13438             
13439             this.hasQuery = true;
13440             
13441             if(this.lastQuery != q || this.alwaysQuery){
13442                 this.lastQuery = q;
13443                 if(this.mode == 'local'){
13444                     this.selectedIndex = -1;
13445                     if(forceAll){
13446                         this.store.clearFilter();
13447                     }else{
13448                         
13449                         if(this.specialFilter){
13450                             this.fireEvent('specialfilter', this);
13451                             this.onLoad();
13452                             return;
13453                         }
13454                         
13455                         this.store.filter(this.displayField, q);
13456                     }
13457                     
13458                     this.store.fireEvent("datachanged", this.store);
13459                     
13460                     this.onLoad();
13461                     
13462                     
13463                 }else{
13464                     
13465                     this.store.baseParams[this.queryParam] = q;
13466                     
13467                     var options = {params : this.getParams(q)};
13468                     
13469                     if(this.loadNext){
13470                         options.add = true;
13471                         options.params.start = this.page * this.pageSize;
13472                     }
13473                     
13474                     this.store.load(options);
13475                     
13476                     /*
13477                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13478                      *  we should expand the list on onLoad
13479                      *  so command out it
13480                      */
13481 //                    this.expand();
13482                 }
13483             }else{
13484                 this.selectedIndex = -1;
13485                 this.onLoad();   
13486             }
13487         }
13488         
13489         this.loadNext = false;
13490     },
13491     
13492     // private
13493     getParams : function(q){
13494         var p = {};
13495         //p[this.queryParam] = q;
13496         
13497         if(this.pageSize){
13498             p.start = 0;
13499             p.limit = this.pageSize;
13500         }
13501         return p;
13502     },
13503
13504     /**
13505      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13506      */
13507     collapse : function(){
13508         if(!this.isExpanded()){
13509             return;
13510         }
13511         
13512         this.list.hide();
13513         
13514         if(this.tickable){
13515             this.hasFocus = false;
13516             this.okBtn.hide();
13517             this.cancelBtn.hide();
13518             this.trigger.show();
13519             
13520             if(this.editable){
13521                 this.tickableInputEl().dom.value = '';
13522                 this.tickableInputEl().blur();
13523             }
13524             
13525         }
13526         
13527         Roo.get(document).un('mousedown', this.collapseIf, this);
13528         Roo.get(document).un('mousewheel', this.collapseIf, this);
13529         if (!this.editable) {
13530             Roo.get(document).un('keydown', this.listKeyPress, this);
13531         }
13532         this.fireEvent('collapse', this);
13533         
13534         this.validate();
13535     },
13536
13537     // private
13538     collapseIf : function(e){
13539         var in_combo  = e.within(this.el);
13540         var in_list =  e.within(this.list);
13541         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13542         
13543         if (in_combo || in_list || is_list) {
13544             //e.stopPropagation();
13545             return;
13546         }
13547         
13548         if(this.tickable){
13549             this.onTickableFooterButtonClick(e, false, false);
13550         }
13551
13552         this.collapse();
13553         
13554     },
13555
13556     /**
13557      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13558      */
13559     expand : function(){
13560        
13561         if(this.isExpanded() || !this.hasFocus){
13562             return;
13563         }
13564         
13565         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13566         this.list.setWidth(lw);
13567         
13568         
13569          Roo.log('expand');
13570         
13571         this.list.show();
13572         
13573         this.restrictHeight();
13574         
13575         if(this.tickable){
13576             
13577             this.tickItems = Roo.apply([], this.item);
13578             
13579             this.okBtn.show();
13580             this.cancelBtn.show();
13581             this.trigger.hide();
13582             
13583             if(this.editable){
13584                 this.tickableInputEl().focus();
13585             }
13586             
13587         }
13588         
13589         Roo.get(document).on('mousedown', this.collapseIf, this);
13590         Roo.get(document).on('mousewheel', this.collapseIf, this);
13591         if (!this.editable) {
13592             Roo.get(document).on('keydown', this.listKeyPress, this);
13593         }
13594         
13595         this.fireEvent('expand', this);
13596     },
13597
13598     // private
13599     // Implements the default empty TriggerField.onTriggerClick function
13600     onTriggerClick : function(e)
13601     {
13602         Roo.log('trigger click');
13603         
13604         if(this.disabled || !this.triggerList){
13605             return;
13606         }
13607         
13608         this.page = 0;
13609         this.loadNext = false;
13610         
13611         if(this.isExpanded()){
13612             this.collapse();
13613             if (!this.blockFocus) {
13614                 this.inputEl().focus();
13615             }
13616             
13617         }else {
13618             this.hasFocus = true;
13619             if(this.triggerAction == 'all') {
13620                 this.doQuery(this.allQuery, true);
13621             } else {
13622                 this.doQuery(this.getRawValue());
13623             }
13624             if (!this.blockFocus) {
13625                 this.inputEl().focus();
13626             }
13627         }
13628     },
13629     
13630     onTickableTriggerClick : function(e)
13631     {
13632         if(this.disabled){
13633             return;
13634         }
13635         
13636         this.page = 0;
13637         this.loadNext = false;
13638         this.hasFocus = true;
13639         
13640         if(this.triggerAction == 'all') {
13641             this.doQuery(this.allQuery, true);
13642         } else {
13643             this.doQuery(this.getRawValue());
13644         }
13645     },
13646     
13647     onSearchFieldClick : function(e)
13648     {
13649         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13650             this.onTickableFooterButtonClick(e, false, false);
13651             return;
13652         }
13653         
13654         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13655             return;
13656         }
13657         
13658         this.page = 0;
13659         this.loadNext = false;
13660         this.hasFocus = true;
13661         
13662         if(this.triggerAction == 'all') {
13663             this.doQuery(this.allQuery, true);
13664         } else {
13665             this.doQuery(this.getRawValue());
13666         }
13667     },
13668     
13669     listKeyPress : function(e)
13670     {
13671         //Roo.log('listkeypress');
13672         // scroll to first matching element based on key pres..
13673         if (e.isSpecialKey()) {
13674             return false;
13675         }
13676         var k = String.fromCharCode(e.getKey()).toUpperCase();
13677         //Roo.log(k);
13678         var match  = false;
13679         var csel = this.view.getSelectedNodes();
13680         var cselitem = false;
13681         if (csel.length) {
13682             var ix = this.view.indexOf(csel[0]);
13683             cselitem  = this.store.getAt(ix);
13684             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13685                 cselitem = false;
13686             }
13687             
13688         }
13689         
13690         this.store.each(function(v) { 
13691             if (cselitem) {
13692                 // start at existing selection.
13693                 if (cselitem.id == v.id) {
13694                     cselitem = false;
13695                 }
13696                 return true;
13697             }
13698                 
13699             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13700                 match = this.store.indexOf(v);
13701                 return false;
13702             }
13703             return true;
13704         }, this);
13705         
13706         if (match === false) {
13707             return true; // no more action?
13708         }
13709         // scroll to?
13710         this.view.select(match);
13711         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13712         sn.scrollIntoView(sn.dom.parentNode, false);
13713     },
13714     
13715     onViewScroll : function(e, t){
13716         
13717         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){
13718             return;
13719         }
13720         
13721         this.hasQuery = true;
13722         
13723         this.loading = this.list.select('.loading', true).first();
13724         
13725         if(this.loading === null){
13726             this.list.createChild({
13727                 tag: 'div',
13728                 cls: 'loading roo-select2-more-results roo-select2-active',
13729                 html: 'Loading more results...'
13730             });
13731             
13732             this.loading = this.list.select('.loading', true).first();
13733             
13734             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13735             
13736             this.loading.hide();
13737         }
13738         
13739         this.loading.show();
13740         
13741         var _combo = this;
13742         
13743         this.page++;
13744         this.loadNext = true;
13745         
13746         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13747         
13748         return;
13749     },
13750     
13751     addItem : function(o)
13752     {   
13753         var dv = ''; // display value
13754         
13755         if (this.displayField) {
13756             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13757         } else {
13758             // this is an error condition!!!
13759             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13760         }
13761         
13762         if(!dv.length){
13763             return;
13764         }
13765         
13766         var choice = this.choices.createChild({
13767             tag: 'li',
13768             cls: 'roo-select2-search-choice',
13769             cn: [
13770                 {
13771                     tag: 'div',
13772                     html: dv
13773                 },
13774                 {
13775                     tag: 'a',
13776                     href: '#',
13777                     cls: 'roo-select2-search-choice-close',
13778                     tabindex: '-1'
13779                 }
13780             ]
13781             
13782         }, this.searchField);
13783         
13784         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13785         
13786         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13787         
13788         this.item.push(o);
13789         
13790         this.lastData = o;
13791         
13792         this.syncValue();
13793         
13794         this.inputEl().dom.value = '';
13795         
13796         this.validate();
13797     },
13798     
13799     onRemoveItem : function(e, _self, o)
13800     {
13801         e.preventDefault();
13802         
13803         this.lastItem = Roo.apply([], this.item);
13804         
13805         var index = this.item.indexOf(o.data) * 1;
13806         
13807         if( index < 0){
13808             Roo.log('not this item?!');
13809             return;
13810         }
13811         
13812         this.item.splice(index, 1);
13813         o.item.remove();
13814         
13815         this.syncValue();
13816         
13817         this.fireEvent('remove', this, e);
13818         
13819         this.validate();
13820         
13821     },
13822     
13823     syncValue : function()
13824     {
13825         if(!this.item.length){
13826             this.clearValue();
13827             return;
13828         }
13829             
13830         var value = [];
13831         var _this = this;
13832         Roo.each(this.item, function(i){
13833             if(_this.valueField){
13834                 value.push(i[_this.valueField]);
13835                 return;
13836             }
13837
13838             value.push(i);
13839         });
13840
13841         this.value = value.join(',');
13842
13843         if(this.hiddenField){
13844             this.hiddenField.dom.value = this.value;
13845         }
13846         
13847         this.store.fireEvent("datachanged", this.store);
13848         
13849         this.validate();
13850     },
13851     
13852     clearItem : function()
13853     {
13854         if(!this.multiple){
13855             return;
13856         }
13857         
13858         this.item = [];
13859         
13860         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13861            c.remove();
13862         });
13863         
13864         this.syncValue();
13865         
13866         this.validate();
13867         
13868         if(this.tickable && !Roo.isTouch){
13869             this.view.refresh();
13870         }
13871     },
13872     
13873     inputEl: function ()
13874     {
13875         if(Roo.isTouch && this.mobileTouchView){
13876             return this.el.select('input.form-control',true).first();
13877         }
13878         
13879         if(this.tickable){
13880             return this.searchField;
13881         }
13882         
13883         return this.el.select('input.form-control',true).first();
13884     },
13885     
13886     
13887     onTickableFooterButtonClick : function(e, btn, el)
13888     {
13889         e.preventDefault();
13890         
13891         this.lastItem = Roo.apply([], this.item);
13892         
13893         if(btn && btn.name == 'cancel'){
13894             this.tickItems = Roo.apply([], this.item);
13895             this.collapse();
13896             return;
13897         }
13898         
13899         this.clearItem();
13900         
13901         var _this = this;
13902         
13903         Roo.each(this.tickItems, function(o){
13904             _this.addItem(o);
13905         });
13906         
13907         this.collapse();
13908         
13909     },
13910     
13911     validate : function()
13912     {
13913         var v = this.getRawValue();
13914         
13915         if(this.multiple){
13916             v = this.getValue();
13917         }
13918         
13919         if(this.disabled || this.allowBlank || v.length){
13920             this.markValid();
13921             return true;
13922         }
13923         
13924         this.markInvalid();
13925         return false;
13926     },
13927     
13928     tickableInputEl : function()
13929     {
13930         if(!this.tickable || !this.editable){
13931             return this.inputEl();
13932         }
13933         
13934         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13935     },
13936     
13937     
13938     getAutoCreateTouchView : function()
13939     {
13940         var id = Roo.id();
13941         
13942         var cfg = {
13943             cls: 'form-group' //input-group
13944         };
13945         
13946         var input =  {
13947             tag: 'input',
13948             id : id,
13949             type : this.inputType,
13950             cls : 'form-control x-combo-noedit',
13951             autocomplete: 'new-password',
13952             placeholder : this.placeholder || '',
13953             readonly : true
13954         };
13955         
13956         if (this.name) {
13957             input.name = this.name;
13958         }
13959         
13960         if (this.size) {
13961             input.cls += ' input-' + this.size;
13962         }
13963         
13964         if (this.disabled) {
13965             input.disabled = true;
13966         }
13967         
13968         var inputblock = {
13969             cls : '',
13970             cn : [
13971                 input
13972             ]
13973         };
13974         
13975         if(this.before){
13976             inputblock.cls += ' input-group';
13977             
13978             inputblock.cn.unshift({
13979                 tag :'span',
13980                 cls : 'input-group-addon',
13981                 html : this.before
13982             });
13983         }
13984         
13985         if(this.removable && !this.multiple){
13986             inputblock.cls += ' roo-removable';
13987             
13988             inputblock.cn.push({
13989                 tag: 'button',
13990                 html : 'x',
13991                 cls : 'roo-combo-removable-btn close'
13992             });
13993         }
13994
13995         if(this.hasFeedback && !this.allowBlank){
13996             
13997             inputblock.cls += ' has-feedback';
13998             
13999             inputblock.cn.push({
14000                 tag: 'span',
14001                 cls: 'glyphicon form-control-feedback'
14002             });
14003             
14004         }
14005         
14006         if (this.after) {
14007             
14008             inputblock.cls += (this.before) ? '' : ' input-group';
14009             
14010             inputblock.cn.push({
14011                 tag :'span',
14012                 cls : 'input-group-addon',
14013                 html : this.after
14014             });
14015         }
14016
14017         var box = {
14018             tag: 'div',
14019             cn: [
14020                 {
14021                     tag: 'input',
14022                     type : 'hidden',
14023                     cls: 'form-hidden-field'
14024                 },
14025                 inputblock
14026             ]
14027             
14028         };
14029         
14030         if(this.multiple){
14031             box = {
14032                 tag: 'div',
14033                 cn: [
14034                     {
14035                         tag: 'input',
14036                         type : 'hidden',
14037                         cls: 'form-hidden-field'
14038                     },
14039                     {
14040                         tag: 'ul',
14041                         cls: 'roo-select2-choices',
14042                         cn:[
14043                             {
14044                                 tag: 'li',
14045                                 cls: 'roo-select2-search-field',
14046                                 cn: [
14047
14048                                     inputblock
14049                                 ]
14050                             }
14051                         ]
14052                     }
14053                 ]
14054             }
14055         };
14056         
14057         var combobox = {
14058             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14059             cn: [
14060                 box
14061             ]
14062         };
14063         
14064         if(!this.multiple && this.showToggleBtn){
14065             
14066             var caret = {
14067                         tag: 'span',
14068                         cls: 'caret'
14069             };
14070             
14071             if (this.caret != false) {
14072                 caret = {
14073                      tag: 'i',
14074                      cls: 'fa fa-' + this.caret
14075                 };
14076                 
14077             }
14078             
14079             combobox.cn.push({
14080                 tag :'span',
14081                 cls : 'input-group-addon btn dropdown-toggle',
14082                 cn : [
14083                     caret,
14084                     {
14085                         tag: 'span',
14086                         cls: 'combobox-clear',
14087                         cn  : [
14088                             {
14089                                 tag : 'i',
14090                                 cls: 'icon-remove'
14091                             }
14092                         ]
14093                     }
14094                 ]
14095
14096             })
14097         }
14098         
14099         if(this.multiple){
14100             combobox.cls += ' roo-select2-container-multi';
14101         }
14102         
14103         var align = this.labelAlign || this.parentLabelAlign();
14104         
14105         cfg.cn = combobox;
14106         
14107         if(this.fieldLabel.length && this.labelWidth){
14108             
14109             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14110             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14111             
14112             cfg.cn = [
14113                 {
14114                    tag : 'i',
14115                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14116                    tooltip : 'This field is required'
14117                 },
14118                 {
14119                     tag: 'label',
14120                     cls : 'control-label ' + lw,
14121                     html : this.fieldLabel
14122
14123                 },
14124                 {
14125                     cls : cw, 
14126                     cn: [
14127                         combobox
14128                     ]
14129                 }
14130             ];
14131             
14132             if(this.indicatorpos == 'right'){
14133                 cfg.cn = [
14134                     {
14135                         tag: 'label',
14136                         cls : 'control-label ' + lw,
14137                         html : this.fieldLabel
14138
14139                     },
14140                     {
14141                        tag : 'i',
14142                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14143                        tooltip : 'This field is required'
14144                     },
14145                     {
14146                         cls : cw, 
14147                         cn: [
14148                             combobox
14149                         ]
14150                     }
14151                 ];
14152             }
14153         }
14154         
14155         var settings = this;
14156         
14157         ['xs','sm','md','lg'].map(function(size){
14158             if (settings[size]) {
14159                 cfg.cls += ' col-' + size + '-' + settings[size];
14160             }
14161         });
14162         
14163         return cfg;
14164     },
14165     
14166     initTouchView : function()
14167     {
14168         this.renderTouchView();
14169         
14170         this.touchViewEl.on('scroll', function(){
14171             this.el.dom.scrollTop = 0;
14172         }, this);
14173         
14174         this.originalValue = this.getValue();
14175         
14176         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14177         
14178         this.inputEl().on("click", this.showTouchView, this);
14179         this.triggerEl.on("click", this.showTouchView, this);
14180         
14181         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14182         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14183         
14184         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14185         
14186         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14187         this.store.on('load', this.onTouchViewLoad, this);
14188         this.store.on('loadexception', this.onTouchViewLoadException, this);
14189         
14190         if(this.hiddenName){
14191             
14192             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14193             
14194             this.hiddenField.dom.value =
14195                 this.hiddenValue !== undefined ? this.hiddenValue :
14196                 this.value !== undefined ? this.value : '';
14197         
14198             this.el.dom.removeAttribute('name');
14199             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14200         }
14201         
14202         if(this.multiple){
14203             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14204             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14205         }
14206         
14207         if(this.removable && !this.multiple){
14208             var close = this.closeTriggerEl();
14209             if(close){
14210                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14211                 close.on('click', this.removeBtnClick, this, close);
14212             }
14213         }
14214         /*
14215          * fix the bug in Safari iOS8
14216          */
14217         this.inputEl().on("focus", function(e){
14218             document.activeElement.blur();
14219         }, this);
14220         
14221         return;
14222         
14223         
14224     },
14225     
14226     renderTouchView : function()
14227     {
14228         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14229         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14230         
14231         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14232         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14233         
14234         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14235         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14236         this.touchViewBodyEl.setStyle('overflow', 'auto');
14237         
14238         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14239         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14240         
14241         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14242         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14243         
14244     },
14245     
14246     showTouchView : function()
14247     {
14248         if(this.disabled){
14249             return;
14250         }
14251         
14252         this.touchViewHeaderEl.hide();
14253
14254         if(this.modalTitle.length){
14255             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14256             this.touchViewHeaderEl.show();
14257         }
14258
14259         this.touchViewEl.show();
14260
14261         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14262         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14263                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14264
14265         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14266
14267         if(this.modalTitle.length){
14268             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14269         }
14270         
14271         this.touchViewBodyEl.setHeight(bodyHeight);
14272
14273         if(this.animate){
14274             var _this = this;
14275             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14276         }else{
14277             this.touchViewEl.addClass('in');
14278         }
14279
14280         this.doTouchViewQuery();
14281         
14282     },
14283     
14284     hideTouchView : function()
14285     {
14286         this.touchViewEl.removeClass('in');
14287
14288         if(this.animate){
14289             var _this = this;
14290             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14291         }else{
14292             this.touchViewEl.setStyle('display', 'none');
14293         }
14294         
14295     },
14296     
14297     setTouchViewValue : function()
14298     {
14299         if(this.multiple){
14300             this.clearItem();
14301         
14302             var _this = this;
14303
14304             Roo.each(this.tickItems, function(o){
14305                 this.addItem(o);
14306             }, this);
14307         }
14308         
14309         this.hideTouchView();
14310     },
14311     
14312     doTouchViewQuery : function()
14313     {
14314         var qe = {
14315             query: '',
14316             forceAll: true,
14317             combo: this,
14318             cancel:false
14319         };
14320         
14321         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14322             return false;
14323         }
14324         
14325         if(!this.alwaysQuery || this.mode == 'local'){
14326             this.onTouchViewLoad();
14327             return;
14328         }
14329         
14330         this.store.load();
14331     },
14332     
14333     onTouchViewBeforeLoad : function(combo,opts)
14334     {
14335         return;
14336     },
14337
14338     // private
14339     onTouchViewLoad : function()
14340     {
14341         if(this.store.getCount() < 1){
14342             this.onTouchViewEmptyResults();
14343             return;
14344         }
14345         
14346         this.clearTouchView();
14347         
14348         var rawValue = this.getRawValue();
14349         
14350         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14351         
14352         this.tickItems = [];
14353         
14354         this.store.data.each(function(d, rowIndex){
14355             var row = this.touchViewListGroup.createChild(template);
14356             
14357             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14358                 row.addClass(d.data.cls);
14359             }
14360             
14361             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14362                 var cfg = {
14363                     data : d.data,
14364                     html : d.data[this.displayField]
14365                 };
14366                 
14367                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14368                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14369                 }
14370             }
14371             row.removeClass('selected');
14372             if(!this.multiple && this.valueField &&
14373                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14374             {
14375                 // radio buttons..
14376                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14377                 row.addClass('selected');
14378             }
14379             
14380             if(this.multiple && this.valueField &&
14381                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14382             {
14383                 
14384                 // checkboxes...
14385                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14386                 this.tickItems.push(d.data);
14387             }
14388             
14389             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14390             
14391         }, this);
14392         
14393         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14394         
14395         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14396
14397         if(this.modalTitle.length){
14398             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14399         }
14400
14401         var listHeight = this.touchViewListGroup.getHeight();
14402         
14403         var _this = this;
14404         
14405         if(firstChecked && listHeight > bodyHeight){
14406             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14407         }
14408         
14409     },
14410     
14411     onTouchViewLoadException : function()
14412     {
14413         this.hideTouchView();
14414     },
14415     
14416     onTouchViewEmptyResults : function()
14417     {
14418         this.clearTouchView();
14419         
14420         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14421         
14422         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14423         
14424     },
14425     
14426     clearTouchView : function()
14427     {
14428         this.touchViewListGroup.dom.innerHTML = '';
14429     },
14430     
14431     onTouchViewClick : function(e, el, o)
14432     {
14433         e.preventDefault();
14434         
14435         var row = o.row;
14436         var rowIndex = o.rowIndex;
14437         
14438         var r = this.store.getAt(rowIndex);
14439         
14440         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14441             
14442             if(!this.multiple){
14443                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14444                     c.dom.removeAttribute('checked');
14445                 }, this);
14446
14447                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14448
14449                 this.setFromData(r.data);
14450
14451                 var close = this.closeTriggerEl();
14452
14453                 if(close){
14454                     close.show();
14455                 }
14456
14457                 this.hideTouchView();
14458
14459                 this.fireEvent('select', this, r, rowIndex);
14460
14461                 return;
14462             }
14463
14464             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14465                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14466                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14467                 return;
14468             }
14469
14470             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14471             this.addItem(r.data);
14472             this.tickItems.push(r.data);
14473         }
14474     }
14475     
14476
14477     /** 
14478     * @cfg {Boolean} grow 
14479     * @hide 
14480     */
14481     /** 
14482     * @cfg {Number} growMin 
14483     * @hide 
14484     */
14485     /** 
14486     * @cfg {Number} growMax 
14487     * @hide 
14488     */
14489     /**
14490      * @hide
14491      * @method autoSize
14492      */
14493 });
14494
14495 Roo.apply(Roo.bootstrap.ComboBox,  {
14496     
14497     header : {
14498         tag: 'div',
14499         cls: 'modal-header',
14500         cn: [
14501             {
14502                 tag: 'h4',
14503                 cls: 'modal-title'
14504             }
14505         ]
14506     },
14507     
14508     body : {
14509         tag: 'div',
14510         cls: 'modal-body',
14511         cn: [
14512             {
14513                 tag: 'ul',
14514                 cls: 'list-group'
14515             }
14516         ]
14517     },
14518     
14519     listItemRadio : {
14520         tag: 'li',
14521         cls: 'list-group-item',
14522         cn: [
14523             {
14524                 tag: 'span',
14525                 cls: 'roo-combobox-list-group-item-value'
14526             },
14527             {
14528                 tag: 'div',
14529                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14530                 cn: [
14531                     {
14532                         tag: 'input',
14533                         type: 'radio'
14534                     },
14535                     {
14536                         tag: 'label'
14537                     }
14538                 ]
14539             }
14540         ]
14541     },
14542     
14543     listItemCheckbox : {
14544         tag: 'li',
14545         cls: 'list-group-item',
14546         cn: [
14547             {
14548                 tag: 'span',
14549                 cls: 'roo-combobox-list-group-item-value'
14550             },
14551             {
14552                 tag: 'div',
14553                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14554                 cn: [
14555                     {
14556                         tag: 'input',
14557                         type: 'checkbox'
14558                     },
14559                     {
14560                         tag: 'label'
14561                     }
14562                 ]
14563             }
14564         ]
14565     },
14566     
14567     emptyResult : {
14568         tag: 'div',
14569         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14570     },
14571     
14572     footer : {
14573         tag: 'div',
14574         cls: 'modal-footer',
14575         cn: [
14576             {
14577                 tag: 'div',
14578                 cls: 'row',
14579                 cn: [
14580                     {
14581                         tag: 'div',
14582                         cls: 'col-xs-6 text-left',
14583                         cn: {
14584                             tag: 'button',
14585                             cls: 'btn btn-danger roo-touch-view-cancel',
14586                             html: 'Cancel'
14587                         }
14588                     },
14589                     {
14590                         tag: 'div',
14591                         cls: 'col-xs-6 text-right',
14592                         cn: {
14593                             tag: 'button',
14594                             cls: 'btn btn-success roo-touch-view-ok',
14595                             html: 'OK'
14596                         }
14597                     }
14598                 ]
14599             }
14600         ]
14601         
14602     }
14603 });
14604
14605 Roo.apply(Roo.bootstrap.ComboBox,  {
14606     
14607     touchViewTemplate : {
14608         tag: 'div',
14609         cls: 'modal fade roo-combobox-touch-view',
14610         cn: [
14611             {
14612                 tag: 'div',
14613                 cls: 'modal-dialog',
14614                 style : 'position:fixed', // we have to fix position....
14615                 cn: [
14616                     {
14617                         tag: 'div',
14618                         cls: 'modal-content',
14619                         cn: [
14620                             Roo.bootstrap.ComboBox.header,
14621                             Roo.bootstrap.ComboBox.body,
14622                             Roo.bootstrap.ComboBox.footer
14623                         ]
14624                     }
14625                 ]
14626             }
14627         ]
14628     }
14629 });/*
14630  * Based on:
14631  * Ext JS Library 1.1.1
14632  * Copyright(c) 2006-2007, Ext JS, LLC.
14633  *
14634  * Originally Released Under LGPL - original licence link has changed is not relivant.
14635  *
14636  * Fork - LGPL
14637  * <script type="text/javascript">
14638  */
14639
14640 /**
14641  * @class Roo.View
14642  * @extends Roo.util.Observable
14643  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14644  * This class also supports single and multi selection modes. <br>
14645  * Create a data model bound view:
14646  <pre><code>
14647  var store = new Roo.data.Store(...);
14648
14649  var view = new Roo.View({
14650     el : "my-element",
14651     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14652  
14653     singleSelect: true,
14654     selectedClass: "ydataview-selected",
14655     store: store
14656  });
14657
14658  // listen for node click?
14659  view.on("click", function(vw, index, node, e){
14660  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14661  });
14662
14663  // load XML data
14664  dataModel.load("foobar.xml");
14665  </code></pre>
14666  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14667  * <br><br>
14668  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14669  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14670  * 
14671  * Note: old style constructor is still suported (container, template, config)
14672  * 
14673  * @constructor
14674  * Create a new View
14675  * @param {Object} config The config object
14676  * 
14677  */
14678 Roo.View = function(config, depreciated_tpl, depreciated_config){
14679     
14680     this.parent = false;
14681     
14682     if (typeof(depreciated_tpl) == 'undefined') {
14683         // new way.. - universal constructor.
14684         Roo.apply(this, config);
14685         this.el  = Roo.get(this.el);
14686     } else {
14687         // old format..
14688         this.el  = Roo.get(config);
14689         this.tpl = depreciated_tpl;
14690         Roo.apply(this, depreciated_config);
14691     }
14692     this.wrapEl  = this.el.wrap().wrap();
14693     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14694     
14695     
14696     if(typeof(this.tpl) == "string"){
14697         this.tpl = new Roo.Template(this.tpl);
14698     } else {
14699         // support xtype ctors..
14700         this.tpl = new Roo.factory(this.tpl, Roo);
14701     }
14702     
14703     
14704     this.tpl.compile();
14705     
14706     /** @private */
14707     this.addEvents({
14708         /**
14709          * @event beforeclick
14710          * Fires before a click is processed. Returns false to cancel the default action.
14711          * @param {Roo.View} this
14712          * @param {Number} index The index of the target node
14713          * @param {HTMLElement} node The target node
14714          * @param {Roo.EventObject} e The raw event object
14715          */
14716             "beforeclick" : true,
14717         /**
14718          * @event click
14719          * Fires when a template node is clicked.
14720          * @param {Roo.View} this
14721          * @param {Number} index The index of the target node
14722          * @param {HTMLElement} node The target node
14723          * @param {Roo.EventObject} e The raw event object
14724          */
14725             "click" : true,
14726         /**
14727          * @event dblclick
14728          * Fires when a template node is double clicked.
14729          * @param {Roo.View} this
14730          * @param {Number} index The index of the target node
14731          * @param {HTMLElement} node The target node
14732          * @param {Roo.EventObject} e The raw event object
14733          */
14734             "dblclick" : true,
14735         /**
14736          * @event contextmenu
14737          * Fires when a template node is right clicked.
14738          * @param {Roo.View} this
14739          * @param {Number} index The index of the target node
14740          * @param {HTMLElement} node The target node
14741          * @param {Roo.EventObject} e The raw event object
14742          */
14743             "contextmenu" : true,
14744         /**
14745          * @event selectionchange
14746          * Fires when the selected nodes change.
14747          * @param {Roo.View} this
14748          * @param {Array} selections Array of the selected nodes
14749          */
14750             "selectionchange" : true,
14751     
14752         /**
14753          * @event beforeselect
14754          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14755          * @param {Roo.View} this
14756          * @param {HTMLElement} node The node to be selected
14757          * @param {Array} selections Array of currently selected nodes
14758          */
14759             "beforeselect" : true,
14760         /**
14761          * @event preparedata
14762          * Fires on every row to render, to allow you to change the data.
14763          * @param {Roo.View} this
14764          * @param {Object} data to be rendered (change this)
14765          */
14766           "preparedata" : true
14767           
14768           
14769         });
14770
14771
14772
14773     this.el.on({
14774         "click": this.onClick,
14775         "dblclick": this.onDblClick,
14776         "contextmenu": this.onContextMenu,
14777         scope:this
14778     });
14779
14780     this.selections = [];
14781     this.nodes = [];
14782     this.cmp = new Roo.CompositeElementLite([]);
14783     if(this.store){
14784         this.store = Roo.factory(this.store, Roo.data);
14785         this.setStore(this.store, true);
14786     }
14787     
14788     if ( this.footer && this.footer.xtype) {
14789            
14790          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14791         
14792         this.footer.dataSource = this.store;
14793         this.footer.container = fctr;
14794         this.footer = Roo.factory(this.footer, Roo);
14795         fctr.insertFirst(this.el);
14796         
14797         // this is a bit insane - as the paging toolbar seems to detach the el..
14798 //        dom.parentNode.parentNode.parentNode
14799          // they get detached?
14800     }
14801     
14802     
14803     Roo.View.superclass.constructor.call(this);
14804     
14805     
14806 };
14807
14808 Roo.extend(Roo.View, Roo.util.Observable, {
14809     
14810      /**
14811      * @cfg {Roo.data.Store} store Data store to load data from.
14812      */
14813     store : false,
14814     
14815     /**
14816      * @cfg {String|Roo.Element} el The container element.
14817      */
14818     el : '',
14819     
14820     /**
14821      * @cfg {String|Roo.Template} tpl The template used by this View 
14822      */
14823     tpl : false,
14824     /**
14825      * @cfg {String} dataName the named area of the template to use as the data area
14826      *                          Works with domtemplates roo-name="name"
14827      */
14828     dataName: false,
14829     /**
14830      * @cfg {String} selectedClass The css class to add to selected nodes
14831      */
14832     selectedClass : "x-view-selected",
14833      /**
14834      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14835      */
14836     emptyText : "",
14837     
14838     /**
14839      * @cfg {String} text to display on mask (default Loading)
14840      */
14841     mask : false,
14842     /**
14843      * @cfg {Boolean} multiSelect Allow multiple selection
14844      */
14845     multiSelect : false,
14846     /**
14847      * @cfg {Boolean} singleSelect Allow single selection
14848      */
14849     singleSelect:  false,
14850     
14851     /**
14852      * @cfg {Boolean} toggleSelect - selecting 
14853      */
14854     toggleSelect : false,
14855     
14856     /**
14857      * @cfg {Boolean} tickable - selecting 
14858      */
14859     tickable : false,
14860     
14861     /**
14862      * Returns the element this view is bound to.
14863      * @return {Roo.Element}
14864      */
14865     getEl : function(){
14866         return this.wrapEl;
14867     },
14868     
14869     
14870
14871     /**
14872      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14873      */
14874     refresh : function(){
14875         //Roo.log('refresh');
14876         var t = this.tpl;
14877         
14878         // if we are using something like 'domtemplate', then
14879         // the what gets used is:
14880         // t.applySubtemplate(NAME, data, wrapping data..)
14881         // the outer template then get' applied with
14882         //     the store 'extra data'
14883         // and the body get's added to the
14884         //      roo-name="data" node?
14885         //      <span class='roo-tpl-{name}'></span> ?????
14886         
14887         
14888         
14889         this.clearSelections();
14890         this.el.update("");
14891         var html = [];
14892         var records = this.store.getRange();
14893         if(records.length < 1) {
14894             
14895             // is this valid??  = should it render a template??
14896             
14897             this.el.update(this.emptyText);
14898             return;
14899         }
14900         var el = this.el;
14901         if (this.dataName) {
14902             this.el.update(t.apply(this.store.meta)); //????
14903             el = this.el.child('.roo-tpl-' + this.dataName);
14904         }
14905         
14906         for(var i = 0, len = records.length; i < len; i++){
14907             var data = this.prepareData(records[i].data, i, records[i]);
14908             this.fireEvent("preparedata", this, data, i, records[i]);
14909             
14910             var d = Roo.apply({}, data);
14911             
14912             if(this.tickable){
14913                 Roo.apply(d, {'roo-id' : Roo.id()});
14914                 
14915                 var _this = this;
14916             
14917                 Roo.each(this.parent.item, function(item){
14918                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14919                         return;
14920                     }
14921                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14922                 });
14923             }
14924             
14925             html[html.length] = Roo.util.Format.trim(
14926                 this.dataName ?
14927                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14928                     t.apply(d)
14929             );
14930         }
14931         
14932         
14933         
14934         el.update(html.join(""));
14935         this.nodes = el.dom.childNodes;
14936         this.updateIndexes(0);
14937     },
14938     
14939
14940     /**
14941      * Function to override to reformat the data that is sent to
14942      * the template for each node.
14943      * DEPRICATED - use the preparedata event handler.
14944      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14945      * a JSON object for an UpdateManager bound view).
14946      */
14947     prepareData : function(data, index, record)
14948     {
14949         this.fireEvent("preparedata", this, data, index, record);
14950         return data;
14951     },
14952
14953     onUpdate : function(ds, record){
14954         // Roo.log('on update');   
14955         this.clearSelections();
14956         var index = this.store.indexOf(record);
14957         var n = this.nodes[index];
14958         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14959         n.parentNode.removeChild(n);
14960         this.updateIndexes(index, index);
14961     },
14962
14963     
14964     
14965 // --------- FIXME     
14966     onAdd : function(ds, records, index)
14967     {
14968         //Roo.log(['on Add', ds, records, index] );        
14969         this.clearSelections();
14970         if(this.nodes.length == 0){
14971             this.refresh();
14972             return;
14973         }
14974         var n = this.nodes[index];
14975         for(var i = 0, len = records.length; i < len; i++){
14976             var d = this.prepareData(records[i].data, i, records[i]);
14977             if(n){
14978                 this.tpl.insertBefore(n, d);
14979             }else{
14980                 
14981                 this.tpl.append(this.el, d);
14982             }
14983         }
14984         this.updateIndexes(index);
14985     },
14986
14987     onRemove : function(ds, record, index){
14988        // Roo.log('onRemove');
14989         this.clearSelections();
14990         var el = this.dataName  ?
14991             this.el.child('.roo-tpl-' + this.dataName) :
14992             this.el; 
14993         
14994         el.dom.removeChild(this.nodes[index]);
14995         this.updateIndexes(index);
14996     },
14997
14998     /**
14999      * Refresh an individual node.
15000      * @param {Number} index
15001      */
15002     refreshNode : function(index){
15003         this.onUpdate(this.store, this.store.getAt(index));
15004     },
15005
15006     updateIndexes : function(startIndex, endIndex){
15007         var ns = this.nodes;
15008         startIndex = startIndex || 0;
15009         endIndex = endIndex || ns.length - 1;
15010         for(var i = startIndex; i <= endIndex; i++){
15011             ns[i].nodeIndex = i;
15012         }
15013     },
15014
15015     /**
15016      * Changes the data store this view uses and refresh the view.
15017      * @param {Store} store
15018      */
15019     setStore : function(store, initial){
15020         if(!initial && this.store){
15021             this.store.un("datachanged", this.refresh);
15022             this.store.un("add", this.onAdd);
15023             this.store.un("remove", this.onRemove);
15024             this.store.un("update", this.onUpdate);
15025             this.store.un("clear", this.refresh);
15026             this.store.un("beforeload", this.onBeforeLoad);
15027             this.store.un("load", this.onLoad);
15028             this.store.un("loadexception", this.onLoad);
15029         }
15030         if(store){
15031           
15032             store.on("datachanged", this.refresh, this);
15033             store.on("add", this.onAdd, this);
15034             store.on("remove", this.onRemove, this);
15035             store.on("update", this.onUpdate, this);
15036             store.on("clear", this.refresh, this);
15037             store.on("beforeload", this.onBeforeLoad, this);
15038             store.on("load", this.onLoad, this);
15039             store.on("loadexception", this.onLoad, this);
15040         }
15041         
15042         if(store){
15043             this.refresh();
15044         }
15045     },
15046     /**
15047      * onbeforeLoad - masks the loading area.
15048      *
15049      */
15050     onBeforeLoad : function(store,opts)
15051     {
15052          //Roo.log('onBeforeLoad');   
15053         if (!opts.add) {
15054             this.el.update("");
15055         }
15056         this.el.mask(this.mask ? this.mask : "Loading" ); 
15057     },
15058     onLoad : function ()
15059     {
15060         this.el.unmask();
15061     },
15062     
15063
15064     /**
15065      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15066      * @param {HTMLElement} node
15067      * @return {HTMLElement} The template node
15068      */
15069     findItemFromChild : function(node){
15070         var el = this.dataName  ?
15071             this.el.child('.roo-tpl-' + this.dataName,true) :
15072             this.el.dom; 
15073         
15074         if(!node || node.parentNode == el){
15075                     return node;
15076             }
15077             var p = node.parentNode;
15078             while(p && p != el){
15079             if(p.parentNode == el){
15080                 return p;
15081             }
15082             p = p.parentNode;
15083         }
15084             return null;
15085     },
15086
15087     /** @ignore */
15088     onClick : function(e){
15089         var item = this.findItemFromChild(e.getTarget());
15090         if(item){
15091             var index = this.indexOf(item);
15092             if(this.onItemClick(item, index, e) !== false){
15093                 this.fireEvent("click", this, index, item, e);
15094             }
15095         }else{
15096             this.clearSelections();
15097         }
15098     },
15099
15100     /** @ignore */
15101     onContextMenu : function(e){
15102         var item = this.findItemFromChild(e.getTarget());
15103         if(item){
15104             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15105         }
15106     },
15107
15108     /** @ignore */
15109     onDblClick : function(e){
15110         var item = this.findItemFromChild(e.getTarget());
15111         if(item){
15112             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15113         }
15114     },
15115
15116     onItemClick : function(item, index, e)
15117     {
15118         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15119             return false;
15120         }
15121         if (this.toggleSelect) {
15122             var m = this.isSelected(item) ? 'unselect' : 'select';
15123             //Roo.log(m);
15124             var _t = this;
15125             _t[m](item, true, false);
15126             return true;
15127         }
15128         if(this.multiSelect || this.singleSelect){
15129             if(this.multiSelect && e.shiftKey && this.lastSelection){
15130                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15131             }else{
15132                 this.select(item, this.multiSelect && e.ctrlKey);
15133                 this.lastSelection = item;
15134             }
15135             
15136             if(!this.tickable){
15137                 e.preventDefault();
15138             }
15139             
15140         }
15141         return true;
15142     },
15143
15144     /**
15145      * Get the number of selected nodes.
15146      * @return {Number}
15147      */
15148     getSelectionCount : function(){
15149         return this.selections.length;
15150     },
15151
15152     /**
15153      * Get the currently selected nodes.
15154      * @return {Array} An array of HTMLElements
15155      */
15156     getSelectedNodes : function(){
15157         return this.selections;
15158     },
15159
15160     /**
15161      * Get the indexes of the selected nodes.
15162      * @return {Array}
15163      */
15164     getSelectedIndexes : function(){
15165         var indexes = [], s = this.selections;
15166         for(var i = 0, len = s.length; i < len; i++){
15167             indexes.push(s[i].nodeIndex);
15168         }
15169         return indexes;
15170     },
15171
15172     /**
15173      * Clear all selections
15174      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15175      */
15176     clearSelections : function(suppressEvent){
15177         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15178             this.cmp.elements = this.selections;
15179             this.cmp.removeClass(this.selectedClass);
15180             this.selections = [];
15181             if(!suppressEvent){
15182                 this.fireEvent("selectionchange", this, this.selections);
15183             }
15184         }
15185     },
15186
15187     /**
15188      * Returns true if the passed node is selected
15189      * @param {HTMLElement/Number} node The node or node index
15190      * @return {Boolean}
15191      */
15192     isSelected : function(node){
15193         var s = this.selections;
15194         if(s.length < 1){
15195             return false;
15196         }
15197         node = this.getNode(node);
15198         return s.indexOf(node) !== -1;
15199     },
15200
15201     /**
15202      * Selects nodes.
15203      * @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
15204      * @param {Boolean} keepExisting (optional) true to keep existing selections
15205      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15206      */
15207     select : function(nodeInfo, keepExisting, suppressEvent){
15208         if(nodeInfo instanceof Array){
15209             if(!keepExisting){
15210                 this.clearSelections(true);
15211             }
15212             for(var i = 0, len = nodeInfo.length; i < len; i++){
15213                 this.select(nodeInfo[i], true, true);
15214             }
15215             return;
15216         } 
15217         var node = this.getNode(nodeInfo);
15218         if(!node || this.isSelected(node)){
15219             return; // already selected.
15220         }
15221         if(!keepExisting){
15222             this.clearSelections(true);
15223         }
15224         
15225         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15226             Roo.fly(node).addClass(this.selectedClass);
15227             this.selections.push(node);
15228             if(!suppressEvent){
15229                 this.fireEvent("selectionchange", this, this.selections);
15230             }
15231         }
15232         
15233         
15234     },
15235       /**
15236      * Unselects nodes.
15237      * @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
15238      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15239      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15240      */
15241     unselect : function(nodeInfo, keepExisting, suppressEvent)
15242     {
15243         if(nodeInfo instanceof Array){
15244             Roo.each(this.selections, function(s) {
15245                 this.unselect(s, nodeInfo);
15246             }, this);
15247             return;
15248         }
15249         var node = this.getNode(nodeInfo);
15250         if(!node || !this.isSelected(node)){
15251             //Roo.log("not selected");
15252             return; // not selected.
15253         }
15254         // fireevent???
15255         var ns = [];
15256         Roo.each(this.selections, function(s) {
15257             if (s == node ) {
15258                 Roo.fly(node).removeClass(this.selectedClass);
15259
15260                 return;
15261             }
15262             ns.push(s);
15263         },this);
15264         
15265         this.selections= ns;
15266         this.fireEvent("selectionchange", this, this.selections);
15267     },
15268
15269     /**
15270      * Gets a template node.
15271      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15272      * @return {HTMLElement} The node or null if it wasn't found
15273      */
15274     getNode : function(nodeInfo){
15275         if(typeof nodeInfo == "string"){
15276             return document.getElementById(nodeInfo);
15277         }else if(typeof nodeInfo == "number"){
15278             return this.nodes[nodeInfo];
15279         }
15280         return nodeInfo;
15281     },
15282
15283     /**
15284      * Gets a range template nodes.
15285      * @param {Number} startIndex
15286      * @param {Number} endIndex
15287      * @return {Array} An array of nodes
15288      */
15289     getNodes : function(start, end){
15290         var ns = this.nodes;
15291         start = start || 0;
15292         end = typeof end == "undefined" ? ns.length - 1 : end;
15293         var nodes = [];
15294         if(start <= end){
15295             for(var i = start; i <= end; i++){
15296                 nodes.push(ns[i]);
15297             }
15298         } else{
15299             for(var i = start; i >= end; i--){
15300                 nodes.push(ns[i]);
15301             }
15302         }
15303         return nodes;
15304     },
15305
15306     /**
15307      * Finds the index of the passed node
15308      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15309      * @return {Number} The index of the node or -1
15310      */
15311     indexOf : function(node){
15312         node = this.getNode(node);
15313         if(typeof node.nodeIndex == "number"){
15314             return node.nodeIndex;
15315         }
15316         var ns = this.nodes;
15317         for(var i = 0, len = ns.length; i < len; i++){
15318             if(ns[i] == node){
15319                 return i;
15320             }
15321         }
15322         return -1;
15323     }
15324 });
15325 /*
15326  * - LGPL
15327  *
15328  * based on jquery fullcalendar
15329  * 
15330  */
15331
15332 Roo.bootstrap = Roo.bootstrap || {};
15333 /**
15334  * @class Roo.bootstrap.Calendar
15335  * @extends Roo.bootstrap.Component
15336  * Bootstrap Calendar class
15337  * @cfg {Boolean} loadMask (true|false) default false
15338  * @cfg {Object} header generate the user specific header of the calendar, default false
15339
15340  * @constructor
15341  * Create a new Container
15342  * @param {Object} config The config object
15343  */
15344
15345
15346
15347 Roo.bootstrap.Calendar = function(config){
15348     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15349      this.addEvents({
15350         /**
15351              * @event select
15352              * Fires when a date is selected
15353              * @param {DatePicker} this
15354              * @param {Date} date The selected date
15355              */
15356         'select': true,
15357         /**
15358              * @event monthchange
15359              * Fires when the displayed month changes 
15360              * @param {DatePicker} this
15361              * @param {Date} date The selected month
15362              */
15363         'monthchange': true,
15364         /**
15365              * @event evententer
15366              * Fires when mouse over an event
15367              * @param {Calendar} this
15368              * @param {event} Event
15369              */
15370         'evententer': true,
15371         /**
15372              * @event eventleave
15373              * Fires when the mouse leaves an
15374              * @param {Calendar} this
15375              * @param {event}
15376              */
15377         'eventleave': true,
15378         /**
15379              * @event eventclick
15380              * Fires when the mouse click an
15381              * @param {Calendar} this
15382              * @param {event}
15383              */
15384         'eventclick': true
15385         
15386     });
15387
15388 };
15389
15390 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15391     
15392      /**
15393      * @cfg {Number} startDay
15394      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15395      */
15396     startDay : 0,
15397     
15398     loadMask : false,
15399     
15400     header : false,
15401       
15402     getAutoCreate : function(){
15403         
15404         
15405         var fc_button = function(name, corner, style, content ) {
15406             return Roo.apply({},{
15407                 tag : 'span',
15408                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15409                          (corner.length ?
15410                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15411                             ''
15412                         ),
15413                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15414                 unselectable: 'on'
15415             });
15416         };
15417         
15418         var header = {};
15419         
15420         if(!this.header){
15421             header = {
15422                 tag : 'table',
15423                 cls : 'fc-header',
15424                 style : 'width:100%',
15425                 cn : [
15426                     {
15427                         tag: 'tr',
15428                         cn : [
15429                             {
15430                                 tag : 'td',
15431                                 cls : 'fc-header-left',
15432                                 cn : [
15433                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15434                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15435                                     { tag: 'span', cls: 'fc-header-space' },
15436                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15437
15438
15439                                 ]
15440                             },
15441
15442                             {
15443                                 tag : 'td',
15444                                 cls : 'fc-header-center',
15445                                 cn : [
15446                                     {
15447                                         tag: 'span',
15448                                         cls: 'fc-header-title',
15449                                         cn : {
15450                                             tag: 'H2',
15451                                             html : 'month / year'
15452                                         }
15453                                     }
15454
15455                                 ]
15456                             },
15457                             {
15458                                 tag : 'td',
15459                                 cls : 'fc-header-right',
15460                                 cn : [
15461                               /*      fc_button('month', 'left', '', 'month' ),
15462                                     fc_button('week', '', '', 'week' ),
15463                                     fc_button('day', 'right', '', 'day' )
15464                                 */    
15465
15466                                 ]
15467                             }
15468
15469                         ]
15470                     }
15471                 ]
15472             };
15473         }
15474         
15475         header = this.header;
15476         
15477        
15478         var cal_heads = function() {
15479             var ret = [];
15480             // fixme - handle this.
15481             
15482             for (var i =0; i < Date.dayNames.length; i++) {
15483                 var d = Date.dayNames[i];
15484                 ret.push({
15485                     tag: 'th',
15486                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15487                     html : d.substring(0,3)
15488                 });
15489                 
15490             }
15491             ret[0].cls += ' fc-first';
15492             ret[6].cls += ' fc-last';
15493             return ret;
15494         };
15495         var cal_cell = function(n) {
15496             return  {
15497                 tag: 'td',
15498                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15499                 cn : [
15500                     {
15501                         cn : [
15502                             {
15503                                 cls: 'fc-day-number',
15504                                 html: 'D'
15505                             },
15506                             {
15507                                 cls: 'fc-day-content',
15508                              
15509                                 cn : [
15510                                      {
15511                                         style: 'position: relative;' // height: 17px;
15512                                     }
15513                                 ]
15514                             }
15515                             
15516                             
15517                         ]
15518                     }
15519                 ]
15520                 
15521             }
15522         };
15523         var cal_rows = function() {
15524             
15525             var ret = [];
15526             for (var r = 0; r < 6; r++) {
15527                 var row= {
15528                     tag : 'tr',
15529                     cls : 'fc-week',
15530                     cn : []
15531                 };
15532                 
15533                 for (var i =0; i < Date.dayNames.length; i++) {
15534                     var d = Date.dayNames[i];
15535                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15536
15537                 }
15538                 row.cn[0].cls+=' fc-first';
15539                 row.cn[0].cn[0].style = 'min-height:90px';
15540                 row.cn[6].cls+=' fc-last';
15541                 ret.push(row);
15542                 
15543             }
15544             ret[0].cls += ' fc-first';
15545             ret[4].cls += ' fc-prev-last';
15546             ret[5].cls += ' fc-last';
15547             return ret;
15548             
15549         };
15550         
15551         var cal_table = {
15552             tag: 'table',
15553             cls: 'fc-border-separate',
15554             style : 'width:100%',
15555             cellspacing  : 0,
15556             cn : [
15557                 { 
15558                     tag: 'thead',
15559                     cn : [
15560                         { 
15561                             tag: 'tr',
15562                             cls : 'fc-first fc-last',
15563                             cn : cal_heads()
15564                         }
15565                     ]
15566                 },
15567                 { 
15568                     tag: 'tbody',
15569                     cn : cal_rows()
15570                 }
15571                   
15572             ]
15573         };
15574          
15575          var cfg = {
15576             cls : 'fc fc-ltr',
15577             cn : [
15578                 header,
15579                 {
15580                     cls : 'fc-content',
15581                     style : "position: relative;",
15582                     cn : [
15583                         {
15584                             cls : 'fc-view fc-view-month fc-grid',
15585                             style : 'position: relative',
15586                             unselectable : 'on',
15587                             cn : [
15588                                 {
15589                                     cls : 'fc-event-container',
15590                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15591                                 },
15592                                 cal_table
15593                             ]
15594                         }
15595                     ]
15596     
15597                 }
15598            ] 
15599             
15600         };
15601         
15602          
15603         
15604         return cfg;
15605     },
15606     
15607     
15608     initEvents : function()
15609     {
15610         if(!this.store){
15611             throw "can not find store for calendar";
15612         }
15613         
15614         var mark = {
15615             tag: "div",
15616             cls:"x-dlg-mask",
15617             style: "text-align:center",
15618             cn: [
15619                 {
15620                     tag: "div",
15621                     style: "background-color:white;width:50%;margin:250 auto",
15622                     cn: [
15623                         {
15624                             tag: "img",
15625                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15626                         },
15627                         {
15628                             tag: "span",
15629                             html: "Loading"
15630                         }
15631                         
15632                     ]
15633                 }
15634             ]
15635         };
15636         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15637         
15638         var size = this.el.select('.fc-content', true).first().getSize();
15639         this.maskEl.setSize(size.width, size.height);
15640         this.maskEl.enableDisplayMode("block");
15641         if(!this.loadMask){
15642             this.maskEl.hide();
15643         }
15644         
15645         this.store = Roo.factory(this.store, Roo.data);
15646         this.store.on('load', this.onLoad, this);
15647         this.store.on('beforeload', this.onBeforeLoad, this);
15648         
15649         this.resize();
15650         
15651         this.cells = this.el.select('.fc-day',true);
15652         //Roo.log(this.cells);
15653         this.textNodes = this.el.query('.fc-day-number');
15654         this.cells.addClassOnOver('fc-state-hover');
15655         
15656         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15657         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15658         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15659         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15660         
15661         this.on('monthchange', this.onMonthChange, this);
15662         
15663         this.update(new Date().clearTime());
15664     },
15665     
15666     resize : function() {
15667         var sz  = this.el.getSize();
15668         
15669         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15670         this.el.select('.fc-day-content div',true).setHeight(34);
15671     },
15672     
15673     
15674     // private
15675     showPrevMonth : function(e){
15676         this.update(this.activeDate.add("mo", -1));
15677     },
15678     showToday : function(e){
15679         this.update(new Date().clearTime());
15680     },
15681     // private
15682     showNextMonth : function(e){
15683         this.update(this.activeDate.add("mo", 1));
15684     },
15685
15686     // private
15687     showPrevYear : function(){
15688         this.update(this.activeDate.add("y", -1));
15689     },
15690
15691     // private
15692     showNextYear : function(){
15693         this.update(this.activeDate.add("y", 1));
15694     },
15695
15696     
15697    // private
15698     update : function(date)
15699     {
15700         var vd = this.activeDate;
15701         this.activeDate = date;
15702 //        if(vd && this.el){
15703 //            var t = date.getTime();
15704 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15705 //                Roo.log('using add remove');
15706 //                
15707 //                this.fireEvent('monthchange', this, date);
15708 //                
15709 //                this.cells.removeClass("fc-state-highlight");
15710 //                this.cells.each(function(c){
15711 //                   if(c.dateValue == t){
15712 //                       c.addClass("fc-state-highlight");
15713 //                       setTimeout(function(){
15714 //                            try{c.dom.firstChild.focus();}catch(e){}
15715 //                       }, 50);
15716 //                       return false;
15717 //                   }
15718 //                   return true;
15719 //                });
15720 //                return;
15721 //            }
15722 //        }
15723         
15724         var days = date.getDaysInMonth();
15725         
15726         var firstOfMonth = date.getFirstDateOfMonth();
15727         var startingPos = firstOfMonth.getDay()-this.startDay;
15728         
15729         if(startingPos < this.startDay){
15730             startingPos += 7;
15731         }
15732         
15733         var pm = date.add(Date.MONTH, -1);
15734         var prevStart = pm.getDaysInMonth()-startingPos;
15735 //        
15736         this.cells = this.el.select('.fc-day',true);
15737         this.textNodes = this.el.query('.fc-day-number');
15738         this.cells.addClassOnOver('fc-state-hover');
15739         
15740         var cells = this.cells.elements;
15741         var textEls = this.textNodes;
15742         
15743         Roo.each(cells, function(cell){
15744             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15745         });
15746         
15747         days += startingPos;
15748
15749         // convert everything to numbers so it's fast
15750         var day = 86400000;
15751         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15752         //Roo.log(d);
15753         //Roo.log(pm);
15754         //Roo.log(prevStart);
15755         
15756         var today = new Date().clearTime().getTime();
15757         var sel = date.clearTime().getTime();
15758         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15759         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15760         var ddMatch = this.disabledDatesRE;
15761         var ddText = this.disabledDatesText;
15762         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15763         var ddaysText = this.disabledDaysText;
15764         var format = this.format;
15765         
15766         var setCellClass = function(cal, cell){
15767             cell.row = 0;
15768             cell.events = [];
15769             cell.more = [];
15770             //Roo.log('set Cell Class');
15771             cell.title = "";
15772             var t = d.getTime();
15773             
15774             //Roo.log(d);
15775             
15776             cell.dateValue = t;
15777             if(t == today){
15778                 cell.className += " fc-today";
15779                 cell.className += " fc-state-highlight";
15780                 cell.title = cal.todayText;
15781             }
15782             if(t == sel){
15783                 // disable highlight in other month..
15784                 //cell.className += " fc-state-highlight";
15785                 
15786             }
15787             // disabling
15788             if(t < min) {
15789                 cell.className = " fc-state-disabled";
15790                 cell.title = cal.minText;
15791                 return;
15792             }
15793             if(t > max) {
15794                 cell.className = " fc-state-disabled";
15795                 cell.title = cal.maxText;
15796                 return;
15797             }
15798             if(ddays){
15799                 if(ddays.indexOf(d.getDay()) != -1){
15800                     cell.title = ddaysText;
15801                     cell.className = " fc-state-disabled";
15802                 }
15803             }
15804             if(ddMatch && format){
15805                 var fvalue = d.dateFormat(format);
15806                 if(ddMatch.test(fvalue)){
15807                     cell.title = ddText.replace("%0", fvalue);
15808                     cell.className = " fc-state-disabled";
15809                 }
15810             }
15811             
15812             if (!cell.initialClassName) {
15813                 cell.initialClassName = cell.dom.className;
15814             }
15815             
15816             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15817         };
15818
15819         var i = 0;
15820         
15821         for(; i < startingPos; i++) {
15822             textEls[i].innerHTML = (++prevStart);
15823             d.setDate(d.getDate()+1);
15824             
15825             cells[i].className = "fc-past fc-other-month";
15826             setCellClass(this, cells[i]);
15827         }
15828         
15829         var intDay = 0;
15830         
15831         for(; i < days; i++){
15832             intDay = i - startingPos + 1;
15833             textEls[i].innerHTML = (intDay);
15834             d.setDate(d.getDate()+1);
15835             
15836             cells[i].className = ''; // "x-date-active";
15837             setCellClass(this, cells[i]);
15838         }
15839         var extraDays = 0;
15840         
15841         for(; i < 42; i++) {
15842             textEls[i].innerHTML = (++extraDays);
15843             d.setDate(d.getDate()+1);
15844             
15845             cells[i].className = "fc-future fc-other-month";
15846             setCellClass(this, cells[i]);
15847         }
15848         
15849         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15850         
15851         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15852         
15853         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15854         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15855         
15856         if(totalRows != 6){
15857             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15858             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15859         }
15860         
15861         this.fireEvent('monthchange', this, date);
15862         
15863         
15864         /*
15865         if(!this.internalRender){
15866             var main = this.el.dom.firstChild;
15867             var w = main.offsetWidth;
15868             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15869             Roo.fly(main).setWidth(w);
15870             this.internalRender = true;
15871             // opera does not respect the auto grow header center column
15872             // then, after it gets a width opera refuses to recalculate
15873             // without a second pass
15874             if(Roo.isOpera && !this.secondPass){
15875                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15876                 this.secondPass = true;
15877                 this.update.defer(10, this, [date]);
15878             }
15879         }
15880         */
15881         
15882     },
15883     
15884     findCell : function(dt) {
15885         dt = dt.clearTime().getTime();
15886         var ret = false;
15887         this.cells.each(function(c){
15888             //Roo.log("check " +c.dateValue + '?=' + dt);
15889             if(c.dateValue == dt){
15890                 ret = c;
15891                 return false;
15892             }
15893             return true;
15894         });
15895         
15896         return ret;
15897     },
15898     
15899     findCells : function(ev) {
15900         var s = ev.start.clone().clearTime().getTime();
15901        // Roo.log(s);
15902         var e= ev.end.clone().clearTime().getTime();
15903        // Roo.log(e);
15904         var ret = [];
15905         this.cells.each(function(c){
15906              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15907             
15908             if(c.dateValue > e){
15909                 return ;
15910             }
15911             if(c.dateValue < s){
15912                 return ;
15913             }
15914             ret.push(c);
15915         });
15916         
15917         return ret;    
15918     },
15919     
15920 //    findBestRow: function(cells)
15921 //    {
15922 //        var ret = 0;
15923 //        
15924 //        for (var i =0 ; i < cells.length;i++) {
15925 //            ret  = Math.max(cells[i].rows || 0,ret);
15926 //        }
15927 //        return ret;
15928 //        
15929 //    },
15930     
15931     
15932     addItem : function(ev)
15933     {
15934         // look for vertical location slot in
15935         var cells = this.findCells(ev);
15936         
15937 //        ev.row = this.findBestRow(cells);
15938         
15939         // work out the location.
15940         
15941         var crow = false;
15942         var rows = [];
15943         for(var i =0; i < cells.length; i++) {
15944             
15945             cells[i].row = cells[0].row;
15946             
15947             if(i == 0){
15948                 cells[i].row = cells[i].row + 1;
15949             }
15950             
15951             if (!crow) {
15952                 crow = {
15953                     start : cells[i],
15954                     end :  cells[i]
15955                 };
15956                 continue;
15957             }
15958             if (crow.start.getY() == cells[i].getY()) {
15959                 // on same row.
15960                 crow.end = cells[i];
15961                 continue;
15962             }
15963             // different row.
15964             rows.push(crow);
15965             crow = {
15966                 start: cells[i],
15967                 end : cells[i]
15968             };
15969             
15970         }
15971         
15972         rows.push(crow);
15973         ev.els = [];
15974         ev.rows = rows;
15975         ev.cells = cells;
15976         
15977         cells[0].events.push(ev);
15978         
15979         this.calevents.push(ev);
15980     },
15981     
15982     clearEvents: function() {
15983         
15984         if(!this.calevents){
15985             return;
15986         }
15987         
15988         Roo.each(this.cells.elements, function(c){
15989             c.row = 0;
15990             c.events = [];
15991             c.more = [];
15992         });
15993         
15994         Roo.each(this.calevents, function(e) {
15995             Roo.each(e.els, function(el) {
15996                 el.un('mouseenter' ,this.onEventEnter, this);
15997                 el.un('mouseleave' ,this.onEventLeave, this);
15998                 el.remove();
15999             },this);
16000         },this);
16001         
16002         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16003             e.remove();
16004         });
16005         
16006     },
16007     
16008     renderEvents: function()
16009     {   
16010         var _this = this;
16011         
16012         this.cells.each(function(c) {
16013             
16014             if(c.row < 5){
16015                 return;
16016             }
16017             
16018             var ev = c.events;
16019             
16020             var r = 4;
16021             if(c.row != c.events.length){
16022                 r = 4 - (4 - (c.row - c.events.length));
16023             }
16024             
16025             c.events = ev.slice(0, r);
16026             c.more = ev.slice(r);
16027             
16028             if(c.more.length && c.more.length == 1){
16029                 c.events.push(c.more.pop());
16030             }
16031             
16032             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16033             
16034         });
16035             
16036         this.cells.each(function(c) {
16037             
16038             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16039             
16040             
16041             for (var e = 0; e < c.events.length; e++){
16042                 var ev = c.events[e];
16043                 var rows = ev.rows;
16044                 
16045                 for(var i = 0; i < rows.length; i++) {
16046                 
16047                     // how many rows should it span..
16048
16049                     var  cfg = {
16050                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16051                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16052
16053                         unselectable : "on",
16054                         cn : [
16055                             {
16056                                 cls: 'fc-event-inner',
16057                                 cn : [
16058     //                                {
16059     //                                  tag:'span',
16060     //                                  cls: 'fc-event-time',
16061     //                                  html : cells.length > 1 ? '' : ev.time
16062     //                                },
16063                                     {
16064                                       tag:'span',
16065                                       cls: 'fc-event-title',
16066                                       html : String.format('{0}', ev.title)
16067                                     }
16068
16069
16070                                 ]
16071                             },
16072                             {
16073                                 cls: 'ui-resizable-handle ui-resizable-e',
16074                                 html : '&nbsp;&nbsp;&nbsp'
16075                             }
16076
16077                         ]
16078                     };
16079
16080                     if (i == 0) {
16081                         cfg.cls += ' fc-event-start';
16082                     }
16083                     if ((i+1) == rows.length) {
16084                         cfg.cls += ' fc-event-end';
16085                     }
16086
16087                     var ctr = _this.el.select('.fc-event-container',true).first();
16088                     var cg = ctr.createChild(cfg);
16089
16090                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16091                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16092
16093                     var r = (c.more.length) ? 1 : 0;
16094                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16095                     cg.setWidth(ebox.right - sbox.x -2);
16096
16097                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16098                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16099                     cg.on('click', _this.onEventClick, _this, ev);
16100
16101                     ev.els.push(cg);
16102                     
16103                 }
16104                 
16105             }
16106             
16107             
16108             if(c.more.length){
16109                 var  cfg = {
16110                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16111                     style : 'position: absolute',
16112                     unselectable : "on",
16113                     cn : [
16114                         {
16115                             cls: 'fc-event-inner',
16116                             cn : [
16117                                 {
16118                                   tag:'span',
16119                                   cls: 'fc-event-title',
16120                                   html : 'More'
16121                                 }
16122
16123
16124                             ]
16125                         },
16126                         {
16127                             cls: 'ui-resizable-handle ui-resizable-e',
16128                             html : '&nbsp;&nbsp;&nbsp'
16129                         }
16130
16131                     ]
16132                 };
16133
16134                 var ctr = _this.el.select('.fc-event-container',true).first();
16135                 var cg = ctr.createChild(cfg);
16136
16137                 var sbox = c.select('.fc-day-content',true).first().getBox();
16138                 var ebox = c.select('.fc-day-content',true).first().getBox();
16139                 //Roo.log(cg);
16140                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16141                 cg.setWidth(ebox.right - sbox.x -2);
16142
16143                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16144                 
16145             }
16146             
16147         });
16148         
16149         
16150         
16151     },
16152     
16153     onEventEnter: function (e, el,event,d) {
16154         this.fireEvent('evententer', this, el, event);
16155     },
16156     
16157     onEventLeave: function (e, el,event,d) {
16158         this.fireEvent('eventleave', this, el, event);
16159     },
16160     
16161     onEventClick: function (e, el,event,d) {
16162         this.fireEvent('eventclick', this, el, event);
16163     },
16164     
16165     onMonthChange: function () {
16166         this.store.load();
16167     },
16168     
16169     onMoreEventClick: function(e, el, more)
16170     {
16171         var _this = this;
16172         
16173         this.calpopover.placement = 'right';
16174         this.calpopover.setTitle('More');
16175         
16176         this.calpopover.setContent('');
16177         
16178         var ctr = this.calpopover.el.select('.popover-content', true).first();
16179         
16180         Roo.each(more, function(m){
16181             var cfg = {
16182                 cls : 'fc-event-hori fc-event-draggable',
16183                 html : m.title
16184             };
16185             var cg = ctr.createChild(cfg);
16186             
16187             cg.on('click', _this.onEventClick, _this, m);
16188         });
16189         
16190         this.calpopover.show(el);
16191         
16192         
16193     },
16194     
16195     onLoad: function () 
16196     {   
16197         this.calevents = [];
16198         var cal = this;
16199         
16200         if(this.store.getCount() > 0){
16201             this.store.data.each(function(d){
16202                cal.addItem({
16203                     id : d.data.id,
16204                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16205                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16206                     time : d.data.start_time,
16207                     title : d.data.title,
16208                     description : d.data.description,
16209                     venue : d.data.venue
16210                 });
16211             });
16212         }
16213         
16214         this.renderEvents();
16215         
16216         if(this.calevents.length && this.loadMask){
16217             this.maskEl.hide();
16218         }
16219     },
16220     
16221     onBeforeLoad: function()
16222     {
16223         this.clearEvents();
16224         if(this.loadMask){
16225             this.maskEl.show();
16226         }
16227     }
16228 });
16229
16230  
16231  /*
16232  * - LGPL
16233  *
16234  * element
16235  * 
16236  */
16237
16238 /**
16239  * @class Roo.bootstrap.Popover
16240  * @extends Roo.bootstrap.Component
16241  * Bootstrap Popover class
16242  * @cfg {String} html contents of the popover   (or false to use children..)
16243  * @cfg {String} title of popover (or false to hide)
16244  * @cfg {String} placement how it is placed
16245  * @cfg {String} trigger click || hover (or false to trigger manually)
16246  * @cfg {String} over what (parent or false to trigger manually.)
16247  * @cfg {Number} delay - delay before showing
16248  
16249  * @constructor
16250  * Create a new Popover
16251  * @param {Object} config The config object
16252  */
16253
16254 Roo.bootstrap.Popover = function(config){
16255     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16256     
16257     this.addEvents({
16258         // raw events
16259          /**
16260          * @event show
16261          * After the popover show
16262          * 
16263          * @param {Roo.bootstrap.Popover} this
16264          */
16265         "show" : true,
16266         /**
16267          * @event hide
16268          * After the popover hide
16269          * 
16270          * @param {Roo.bootstrap.Popover} this
16271          */
16272         "hide" : true
16273     });
16274 };
16275
16276 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16277     
16278     title: 'Fill in a title',
16279     html: false,
16280     
16281     placement : 'right',
16282     trigger : 'hover', // hover
16283     
16284     delay : 0,
16285     
16286     over: 'parent',
16287     
16288     can_build_overlaid : false,
16289     
16290     getChildContainer : function()
16291     {
16292         return this.el.select('.popover-content',true).first();
16293     },
16294     
16295     getAutoCreate : function(){
16296          
16297         var cfg = {
16298            cls : 'popover roo-dynamic',
16299            style: 'display:block',
16300            cn : [
16301                 {
16302                     cls : 'arrow'
16303                 },
16304                 {
16305                     cls : 'popover-inner',
16306                     cn : [
16307                         {
16308                             tag: 'h3',
16309                             cls: 'popover-title',
16310                             html : this.title
16311                         },
16312                         {
16313                             cls : 'popover-content',
16314                             html : this.html
16315                         }
16316                     ]
16317                     
16318                 }
16319            ]
16320         };
16321         
16322         return cfg;
16323     },
16324     setTitle: function(str)
16325     {
16326         this.title = str;
16327         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16328     },
16329     setContent: function(str)
16330     {
16331         this.html = str;
16332         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16333     },
16334     // as it get's added to the bottom of the page.
16335     onRender : function(ct, position)
16336     {
16337         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16338         if(!this.el){
16339             var cfg = Roo.apply({},  this.getAutoCreate());
16340             cfg.id = Roo.id();
16341             
16342             if (this.cls) {
16343                 cfg.cls += ' ' + this.cls;
16344             }
16345             if (this.style) {
16346                 cfg.style = this.style;
16347             }
16348             //Roo.log("adding to ");
16349             this.el = Roo.get(document.body).createChild(cfg, position);
16350 //            Roo.log(this.el);
16351         }
16352         this.initEvents();
16353     },
16354     
16355     initEvents : function()
16356     {
16357         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16358         this.el.enableDisplayMode('block');
16359         this.el.hide();
16360         if (this.over === false) {
16361             return; 
16362         }
16363         if (this.triggers === false) {
16364             return;
16365         }
16366         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16367         var triggers = this.trigger ? this.trigger.split(' ') : [];
16368         Roo.each(triggers, function(trigger) {
16369         
16370             if (trigger == 'click') {
16371                 on_el.on('click', this.toggle, this);
16372             } else if (trigger != 'manual') {
16373                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16374                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16375       
16376                 on_el.on(eventIn  ,this.enter, this);
16377                 on_el.on(eventOut, this.leave, this);
16378             }
16379         }, this);
16380         
16381     },
16382     
16383     
16384     // private
16385     timeout : null,
16386     hoverState : null,
16387     
16388     toggle : function () {
16389         this.hoverState == 'in' ? this.leave() : this.enter();
16390     },
16391     
16392     enter : function () {
16393         
16394         clearTimeout(this.timeout);
16395     
16396         this.hoverState = 'in';
16397     
16398         if (!this.delay || !this.delay.show) {
16399             this.show();
16400             return;
16401         }
16402         var _t = this;
16403         this.timeout = setTimeout(function () {
16404             if (_t.hoverState == 'in') {
16405                 _t.show();
16406             }
16407         }, this.delay.show)
16408     },
16409     
16410     leave : function() {
16411         clearTimeout(this.timeout);
16412     
16413         this.hoverState = 'out';
16414     
16415         if (!this.delay || !this.delay.hide) {
16416             this.hide();
16417             return;
16418         }
16419         var _t = this;
16420         this.timeout = setTimeout(function () {
16421             if (_t.hoverState == 'out') {
16422                 _t.hide();
16423             }
16424         }, this.delay.hide)
16425     },
16426     
16427     show : function (on_el)
16428     {
16429         if (!on_el) {
16430             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16431         }
16432         
16433         // set content.
16434         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16435         if (this.html !== false) {
16436             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16437         }
16438         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16439         if (!this.title.length) {
16440             this.el.select('.popover-title',true).hide();
16441         }
16442         
16443         var placement = typeof this.placement == 'function' ?
16444             this.placement.call(this, this.el, on_el) :
16445             this.placement;
16446             
16447         var autoToken = /\s?auto?\s?/i;
16448         var autoPlace = autoToken.test(placement);
16449         if (autoPlace) {
16450             placement = placement.replace(autoToken, '') || 'top';
16451         }
16452         
16453         //this.el.detach()
16454         //this.el.setXY([0,0]);
16455         this.el.show();
16456         this.el.dom.style.display='block';
16457         this.el.addClass(placement);
16458         
16459         //this.el.appendTo(on_el);
16460         
16461         var p = this.getPosition();
16462         var box = this.el.getBox();
16463         
16464         if (autoPlace) {
16465             // fixme..
16466         }
16467         var align = Roo.bootstrap.Popover.alignment[placement];
16468         this.el.alignTo(on_el, align[0],align[1]);
16469         //var arrow = this.el.select('.arrow',true).first();
16470         //arrow.set(align[2], 
16471         
16472         this.el.addClass('in');
16473         
16474         
16475         if (this.el.hasClass('fade')) {
16476             // fade it?
16477         }
16478         
16479         this.hoverState = 'in';
16480         
16481         this.fireEvent('show', this);
16482         
16483     },
16484     hide : function()
16485     {
16486         this.el.setXY([0,0]);
16487         this.el.removeClass('in');
16488         this.el.hide();
16489         this.hoverState = null;
16490         
16491         this.fireEvent('hide', this);
16492     }
16493     
16494 });
16495
16496 Roo.bootstrap.Popover.alignment = {
16497     'left' : ['r-l', [-10,0], 'right'],
16498     'right' : ['l-r', [10,0], 'left'],
16499     'bottom' : ['t-b', [0,10], 'top'],
16500     'top' : [ 'b-t', [0,-10], 'bottom']
16501 };
16502
16503  /*
16504  * - LGPL
16505  *
16506  * Progress
16507  * 
16508  */
16509
16510 /**
16511  * @class Roo.bootstrap.Progress
16512  * @extends Roo.bootstrap.Component
16513  * Bootstrap Progress class
16514  * @cfg {Boolean} striped striped of the progress bar
16515  * @cfg {Boolean} active animated of the progress bar
16516  * 
16517  * 
16518  * @constructor
16519  * Create a new Progress
16520  * @param {Object} config The config object
16521  */
16522
16523 Roo.bootstrap.Progress = function(config){
16524     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16525 };
16526
16527 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16528     
16529     striped : false,
16530     active: false,
16531     
16532     getAutoCreate : function(){
16533         var cfg = {
16534             tag: 'div',
16535             cls: 'progress'
16536         };
16537         
16538         
16539         if(this.striped){
16540             cfg.cls += ' progress-striped';
16541         }
16542       
16543         if(this.active){
16544             cfg.cls += ' active';
16545         }
16546         
16547         
16548         return cfg;
16549     }
16550    
16551 });
16552
16553  
16554
16555  /*
16556  * - LGPL
16557  *
16558  * ProgressBar
16559  * 
16560  */
16561
16562 /**
16563  * @class Roo.bootstrap.ProgressBar
16564  * @extends Roo.bootstrap.Component
16565  * Bootstrap ProgressBar class
16566  * @cfg {Number} aria_valuenow aria-value now
16567  * @cfg {Number} aria_valuemin aria-value min
16568  * @cfg {Number} aria_valuemax aria-value max
16569  * @cfg {String} label label for the progress bar
16570  * @cfg {String} panel (success | info | warning | danger )
16571  * @cfg {String} role role of the progress bar
16572  * @cfg {String} sr_only text
16573  * 
16574  * 
16575  * @constructor
16576  * Create a new ProgressBar
16577  * @param {Object} config The config object
16578  */
16579
16580 Roo.bootstrap.ProgressBar = function(config){
16581     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16582 };
16583
16584 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16585     
16586     aria_valuenow : 0,
16587     aria_valuemin : 0,
16588     aria_valuemax : 100,
16589     label : false,
16590     panel : false,
16591     role : false,
16592     sr_only: false,
16593     
16594     getAutoCreate : function()
16595     {
16596         
16597         var cfg = {
16598             tag: 'div',
16599             cls: 'progress-bar',
16600             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16601         };
16602         
16603         if(this.sr_only){
16604             cfg.cn = {
16605                 tag: 'span',
16606                 cls: 'sr-only',
16607                 html: this.sr_only
16608             }
16609         }
16610         
16611         if(this.role){
16612             cfg.role = this.role;
16613         }
16614         
16615         if(this.aria_valuenow){
16616             cfg['aria-valuenow'] = this.aria_valuenow;
16617         }
16618         
16619         if(this.aria_valuemin){
16620             cfg['aria-valuemin'] = this.aria_valuemin;
16621         }
16622         
16623         if(this.aria_valuemax){
16624             cfg['aria-valuemax'] = this.aria_valuemax;
16625         }
16626         
16627         if(this.label && !this.sr_only){
16628             cfg.html = this.label;
16629         }
16630         
16631         if(this.panel){
16632             cfg.cls += ' progress-bar-' + this.panel;
16633         }
16634         
16635         return cfg;
16636     },
16637     
16638     update : function(aria_valuenow)
16639     {
16640         this.aria_valuenow = aria_valuenow;
16641         
16642         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16643     }
16644    
16645 });
16646
16647  
16648
16649  /*
16650  * - LGPL
16651  *
16652  * column
16653  * 
16654  */
16655
16656 /**
16657  * @class Roo.bootstrap.TabGroup
16658  * @extends Roo.bootstrap.Column
16659  * Bootstrap Column class
16660  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16661  * @cfg {Boolean} carousel true to make the group behave like a carousel
16662  * @cfg {Boolean} bullets show bullets for the panels
16663  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16664  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16665  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16666  * @cfg {Boolean} showarrow (true|false) show arrow default true
16667  * 
16668  * @constructor
16669  * Create a new TabGroup
16670  * @param {Object} config The config object
16671  */
16672
16673 Roo.bootstrap.TabGroup = function(config){
16674     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16675     if (!this.navId) {
16676         this.navId = Roo.id();
16677     }
16678     this.tabs = [];
16679     Roo.bootstrap.TabGroup.register(this);
16680     
16681 };
16682
16683 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16684     
16685     carousel : false,
16686     transition : false,
16687     bullets : 0,
16688     timer : 0,
16689     autoslide : false,
16690     slideFn : false,
16691     slideOnTouch : false,
16692     showarrow : true,
16693     
16694     getAutoCreate : function()
16695     {
16696         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16697         
16698         cfg.cls += ' tab-content';
16699         
16700         if (this.carousel) {
16701             cfg.cls += ' carousel slide';
16702             
16703             cfg.cn = [{
16704                cls : 'carousel-inner',
16705                cn : []
16706             }];
16707         
16708             if(this.bullets  && !Roo.isTouch){
16709                 
16710                 var bullets = {
16711                     cls : 'carousel-bullets',
16712                     cn : []
16713                 };
16714                
16715                 if(this.bullets_cls){
16716                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16717                 }
16718                 
16719                 bullets.cn.push({
16720                     cls : 'clear'
16721                 });
16722                 
16723                 cfg.cn[0].cn.push(bullets);
16724             }
16725             
16726             if(this.showarrow){
16727                 cfg.cn[0].cn.push({
16728                     tag : 'div',
16729                     class : 'carousel-arrow',
16730                     cn : [
16731                         {
16732                             tag : 'div',
16733                             class : 'carousel-prev',
16734                             cn : [
16735                                 {
16736                                     tag : 'i',
16737                                     class : 'fa fa-chevron-left'
16738                                 }
16739                             ]
16740                         },
16741                         {
16742                             tag : 'div',
16743                             class : 'carousel-next',
16744                             cn : [
16745                                 {
16746                                     tag : 'i',
16747                                     class : 'fa fa-chevron-right'
16748                                 }
16749                             ]
16750                         }
16751                     ]
16752                 });
16753             }
16754             
16755         }
16756         
16757         return cfg;
16758     },
16759     
16760     initEvents:  function()
16761     {
16762         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16763             this.el.on("touchstart", this.onTouchStart, this);
16764         }
16765         
16766         if(this.autoslide){
16767             var _this = this;
16768             
16769             this.slideFn = window.setInterval(function() {
16770                 _this.showPanelNext();
16771             }, this.timer);
16772         }
16773         
16774         if(this.showarrow){
16775             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16776             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16777         }
16778         
16779         
16780     },
16781     
16782     onTouchStart : function(e, el, o)
16783     {
16784         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16785             return;
16786         }
16787         
16788         this.showPanelNext();
16789     },
16790     
16791     getChildContainer : function()
16792     {
16793         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16794     },
16795     
16796     /**
16797     * register a Navigation item
16798     * @param {Roo.bootstrap.NavItem} the navitem to add
16799     */
16800     register : function(item)
16801     {
16802         this.tabs.push( item);
16803         item.navId = this.navId; // not really needed..
16804         this.addBullet();
16805     
16806     },
16807     
16808     getActivePanel : function()
16809     {
16810         var r = false;
16811         Roo.each(this.tabs, function(t) {
16812             if (t.active) {
16813                 r = t;
16814                 return false;
16815             }
16816             return null;
16817         });
16818         return r;
16819         
16820     },
16821     getPanelByName : function(n)
16822     {
16823         var r = false;
16824         Roo.each(this.tabs, function(t) {
16825             if (t.tabId == n) {
16826                 r = t;
16827                 return false;
16828             }
16829             return null;
16830         });
16831         return r;
16832     },
16833     indexOfPanel : function(p)
16834     {
16835         var r = false;
16836         Roo.each(this.tabs, function(t,i) {
16837             if (t.tabId == p.tabId) {
16838                 r = i;
16839                 return false;
16840             }
16841             return null;
16842         });
16843         return r;
16844     },
16845     /**
16846      * show a specific panel
16847      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16848      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16849      */
16850     showPanel : function (pan)
16851     {
16852         if(this.transition || typeof(pan) == 'undefined'){
16853             Roo.log("waiting for the transitionend");
16854             return;
16855         }
16856         
16857         if (typeof(pan) == 'number') {
16858             pan = this.tabs[pan];
16859         }
16860         
16861         if (typeof(pan) == 'string') {
16862             pan = this.getPanelByName(pan);
16863         }
16864         
16865         var cur = this.getActivePanel();
16866         
16867         if(!pan || !cur){
16868             Roo.log('pan or acitve pan is undefined');
16869             return false;
16870         }
16871         
16872         if (pan.tabId == this.getActivePanel().tabId) {
16873             return true;
16874         }
16875         
16876         if (false === cur.fireEvent('beforedeactivate')) {
16877             return false;
16878         }
16879         
16880         if(this.bullets > 0 && !Roo.isTouch){
16881             this.setActiveBullet(this.indexOfPanel(pan));
16882         }
16883         
16884         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16885             
16886             this.transition = true;
16887             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16888             var lr = dir == 'next' ? 'left' : 'right';
16889             pan.el.addClass(dir); // or prev
16890             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16891             cur.el.addClass(lr); // or right
16892             pan.el.addClass(lr);
16893             
16894             var _this = this;
16895             cur.el.on('transitionend', function() {
16896                 Roo.log("trans end?");
16897                 
16898                 pan.el.removeClass([lr,dir]);
16899                 pan.setActive(true);
16900                 
16901                 cur.el.removeClass([lr]);
16902                 cur.setActive(false);
16903                 
16904                 _this.transition = false;
16905                 
16906             }, this, { single:  true } );
16907             
16908             return true;
16909         }
16910         
16911         cur.setActive(false);
16912         pan.setActive(true);
16913         
16914         return true;
16915         
16916     },
16917     showPanelNext : function()
16918     {
16919         var i = this.indexOfPanel(this.getActivePanel());
16920         
16921         if (i >= this.tabs.length - 1 && !this.autoslide) {
16922             return;
16923         }
16924         
16925         if (i >= this.tabs.length - 1 && this.autoslide) {
16926             i = -1;
16927         }
16928         
16929         this.showPanel(this.tabs[i+1]);
16930     },
16931     
16932     showPanelPrev : function()
16933     {
16934         var i = this.indexOfPanel(this.getActivePanel());
16935         
16936         if (i  < 1 && !this.autoslide) {
16937             return;
16938         }
16939         
16940         if (i < 1 && this.autoslide) {
16941             i = this.tabs.length;
16942         }
16943         
16944         this.showPanel(this.tabs[i-1]);
16945     },
16946     
16947     
16948     addBullet: function()
16949     {
16950         if(!this.bullets || Roo.isTouch){
16951             return;
16952         }
16953         var ctr = this.el.select('.carousel-bullets',true).first();
16954         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16955         var bullet = ctr.createChild({
16956             cls : 'bullet bullet-' + i
16957         },ctr.dom.lastChild);
16958         
16959         
16960         var _this = this;
16961         
16962         bullet.on('click', (function(e, el, o, ii, t){
16963
16964             e.preventDefault();
16965
16966             this.showPanel(ii);
16967
16968             if(this.autoslide && this.slideFn){
16969                 clearInterval(this.slideFn);
16970                 this.slideFn = window.setInterval(function() {
16971                     _this.showPanelNext();
16972                 }, this.timer);
16973             }
16974
16975         }).createDelegate(this, [i, bullet], true));
16976                 
16977         
16978     },
16979      
16980     setActiveBullet : function(i)
16981     {
16982         if(Roo.isTouch){
16983             return;
16984         }
16985         
16986         Roo.each(this.el.select('.bullet', true).elements, function(el){
16987             el.removeClass('selected');
16988         });
16989
16990         var bullet = this.el.select('.bullet-' + i, true).first();
16991         
16992         if(!bullet){
16993             return;
16994         }
16995         
16996         bullet.addClass('selected');
16997     }
16998     
16999     
17000   
17001 });
17002
17003  
17004
17005  
17006  
17007 Roo.apply(Roo.bootstrap.TabGroup, {
17008     
17009     groups: {},
17010      /**
17011     * register a Navigation Group
17012     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17013     */
17014     register : function(navgrp)
17015     {
17016         this.groups[navgrp.navId] = navgrp;
17017         
17018     },
17019     /**
17020     * fetch a Navigation Group based on the navigation ID
17021     * if one does not exist , it will get created.
17022     * @param {string} the navgroup to add
17023     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17024     */
17025     get: function(navId) {
17026         if (typeof(this.groups[navId]) == 'undefined') {
17027             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17028         }
17029         return this.groups[navId] ;
17030     }
17031     
17032     
17033     
17034 });
17035
17036  /*
17037  * - LGPL
17038  *
17039  * TabPanel
17040  * 
17041  */
17042
17043 /**
17044  * @class Roo.bootstrap.TabPanel
17045  * @extends Roo.bootstrap.Component
17046  * Bootstrap TabPanel class
17047  * @cfg {Boolean} active panel active
17048  * @cfg {String} html panel content
17049  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17050  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17051  * @cfg {String} href click to link..
17052  * 
17053  * 
17054  * @constructor
17055  * Create a new TabPanel
17056  * @param {Object} config The config object
17057  */
17058
17059 Roo.bootstrap.TabPanel = function(config){
17060     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17061     this.addEvents({
17062         /**
17063              * @event changed
17064              * Fires when the active status changes
17065              * @param {Roo.bootstrap.TabPanel} this
17066              * @param {Boolean} state the new state
17067             
17068          */
17069         'changed': true,
17070         /**
17071              * @event beforedeactivate
17072              * Fires before a tab is de-activated - can be used to do validation on a form.
17073              * @param {Roo.bootstrap.TabPanel} this
17074              * @return {Boolean} false if there is an error
17075             
17076          */
17077         'beforedeactivate': true
17078      });
17079     
17080     this.tabId = this.tabId || Roo.id();
17081   
17082 };
17083
17084 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17085     
17086     active: false,
17087     html: false,
17088     tabId: false,
17089     navId : false,
17090     href : '',
17091     
17092     getAutoCreate : function(){
17093         var cfg = {
17094             tag: 'div',
17095             // item is needed for carousel - not sure if it has any effect otherwise
17096             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17097             html: this.html || ''
17098         };
17099         
17100         if(this.active){
17101             cfg.cls += ' active';
17102         }
17103         
17104         if(this.tabId){
17105             cfg.tabId = this.tabId;
17106         }
17107         
17108         
17109         return cfg;
17110     },
17111     
17112     initEvents:  function()
17113     {
17114         var p = this.parent();
17115         this.navId = this.navId || p.navId;
17116         
17117         if (typeof(this.navId) != 'undefined') {
17118             // not really needed.. but just in case.. parent should be a NavGroup.
17119             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17120             
17121             tg.register(this);
17122             
17123             var i = tg.tabs.length - 1;
17124             
17125             if(this.active && tg.bullets > 0 && i < tg.bullets){
17126                 tg.setActiveBullet(i);
17127             }
17128         }
17129         
17130         if(this.href.length){
17131             this.el.on('click', this.onClick, this);
17132         }
17133         
17134     },
17135     
17136     onRender : function(ct, position)
17137     {
17138        // Roo.log("Call onRender: " + this.xtype);
17139         
17140         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17141         
17142         
17143         
17144         
17145         
17146     },
17147     
17148     setActive: function(state)
17149     {
17150         Roo.log("panel - set active " + this.tabId + "=" + state);
17151         
17152         this.active = state;
17153         if (!state) {
17154             this.el.removeClass('active');
17155             
17156         } else  if (!this.el.hasClass('active')) {
17157             this.el.addClass('active');
17158         }
17159         
17160         this.fireEvent('changed', this, state);
17161     },
17162     
17163     onClick: function(e)
17164     {
17165         e.preventDefault();
17166         
17167         window.location.href = this.href;
17168     }
17169     
17170     
17171 });
17172  
17173
17174  
17175
17176  /*
17177  * - LGPL
17178  *
17179  * DateField
17180  * 
17181  */
17182
17183 /**
17184  * @class Roo.bootstrap.DateField
17185  * @extends Roo.bootstrap.Input
17186  * Bootstrap DateField class
17187  * @cfg {Number} weekStart default 0
17188  * @cfg {String} viewMode default empty, (months|years)
17189  * @cfg {String} minViewMode default empty, (months|years)
17190  * @cfg {Number} startDate default -Infinity
17191  * @cfg {Number} endDate default Infinity
17192  * @cfg {Boolean} todayHighlight default false
17193  * @cfg {Boolean} todayBtn default false
17194  * @cfg {Boolean} calendarWeeks default false
17195  * @cfg {Object} daysOfWeekDisabled default empty
17196  * @cfg {Boolean} singleMode default false (true | false)
17197  * 
17198  * @cfg {Boolean} keyboardNavigation default true
17199  * @cfg {String} language default en
17200  * 
17201  * @constructor
17202  * Create a new DateField
17203  * @param {Object} config The config object
17204  */
17205
17206 Roo.bootstrap.DateField = function(config){
17207     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17208      this.addEvents({
17209             /**
17210              * @event show
17211              * Fires when this field show.
17212              * @param {Roo.bootstrap.DateField} this
17213              * @param {Mixed} date The date value
17214              */
17215             show : true,
17216             /**
17217              * @event show
17218              * Fires when this field hide.
17219              * @param {Roo.bootstrap.DateField} this
17220              * @param {Mixed} date The date value
17221              */
17222             hide : true,
17223             /**
17224              * @event select
17225              * Fires when select a date.
17226              * @param {Roo.bootstrap.DateField} this
17227              * @param {Mixed} date The date value
17228              */
17229             select : true,
17230             /**
17231              * @event beforeselect
17232              * Fires when before select a date.
17233              * @param {Roo.bootstrap.DateField} this
17234              * @param {Mixed} date The date value
17235              */
17236             beforeselect : true
17237         });
17238 };
17239
17240 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17241     
17242     /**
17243      * @cfg {String} format
17244      * The default date format string which can be overriden for localization support.  The format must be
17245      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17246      */
17247     format : "m/d/y",
17248     /**
17249      * @cfg {String} altFormats
17250      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17251      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17252      */
17253     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17254     
17255     weekStart : 0,
17256     
17257     viewMode : '',
17258     
17259     minViewMode : '',
17260     
17261     todayHighlight : false,
17262     
17263     todayBtn: false,
17264     
17265     language: 'en',
17266     
17267     keyboardNavigation: true,
17268     
17269     calendarWeeks: false,
17270     
17271     startDate: -Infinity,
17272     
17273     endDate: Infinity,
17274     
17275     daysOfWeekDisabled: [],
17276     
17277     _events: [],
17278     
17279     singleMode : false,
17280     
17281     UTCDate: function()
17282     {
17283         return new Date(Date.UTC.apply(Date, arguments));
17284     },
17285     
17286     UTCToday: function()
17287     {
17288         var today = new Date();
17289         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17290     },
17291     
17292     getDate: function() {
17293             var d = this.getUTCDate();
17294             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17295     },
17296     
17297     getUTCDate: function() {
17298             return this.date;
17299     },
17300     
17301     setDate: function(d) {
17302             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17303     },
17304     
17305     setUTCDate: function(d) {
17306             this.date = d;
17307             this.setValue(this.formatDate(this.date));
17308     },
17309         
17310     onRender: function(ct, position)
17311     {
17312         
17313         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17314         
17315         this.language = this.language || 'en';
17316         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17317         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17318         
17319         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17320         this.format = this.format || 'm/d/y';
17321         this.isInline = false;
17322         this.isInput = true;
17323         this.component = this.el.select('.add-on', true).first() || false;
17324         this.component = (this.component && this.component.length === 0) ? false : this.component;
17325         this.hasInput = this.component && this.inputEL().length;
17326         
17327         if (typeof(this.minViewMode === 'string')) {
17328             switch (this.minViewMode) {
17329                 case 'months':
17330                     this.minViewMode = 1;
17331                     break;
17332                 case 'years':
17333                     this.minViewMode = 2;
17334                     break;
17335                 default:
17336                     this.minViewMode = 0;
17337                     break;
17338             }
17339         }
17340         
17341         if (typeof(this.viewMode === 'string')) {
17342             switch (this.viewMode) {
17343                 case 'months':
17344                     this.viewMode = 1;
17345                     break;
17346                 case 'years':
17347                     this.viewMode = 2;
17348                     break;
17349                 default:
17350                     this.viewMode = 0;
17351                     break;
17352             }
17353         }
17354                 
17355         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17356         
17357 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17358         
17359         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17360         
17361         this.picker().on('mousedown', this.onMousedown, this);
17362         this.picker().on('click', this.onClick, this);
17363         
17364         this.picker().addClass('datepicker-dropdown');
17365         
17366         this.startViewMode = this.viewMode;
17367         
17368         if(this.singleMode){
17369             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17370                 v.setVisibilityMode(Roo.Element.DISPLAY);
17371                 v.hide();
17372             });
17373             
17374             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17375                 v.setStyle('width', '189px');
17376             });
17377         }
17378         
17379         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17380             if(!this.calendarWeeks){
17381                 v.remove();
17382                 return;
17383             }
17384             
17385             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17386             v.attr('colspan', function(i, val){
17387                 return parseInt(val) + 1;
17388             });
17389         });
17390                         
17391         
17392         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17393         
17394         this.setStartDate(this.startDate);
17395         this.setEndDate(this.endDate);
17396         
17397         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17398         
17399         this.fillDow();
17400         this.fillMonths();
17401         this.update();
17402         this.showMode();
17403         
17404         if(this.isInline) {
17405             this.show();
17406         }
17407     },
17408     
17409     picker : function()
17410     {
17411         return this.pickerEl;
17412 //        return this.el.select('.datepicker', true).first();
17413     },
17414     
17415     fillDow: function()
17416     {
17417         var dowCnt = this.weekStart;
17418         
17419         var dow = {
17420             tag: 'tr',
17421             cn: [
17422                 
17423             ]
17424         };
17425         
17426         if(this.calendarWeeks){
17427             dow.cn.push({
17428                 tag: 'th',
17429                 cls: 'cw',
17430                 html: '&nbsp;'
17431             })
17432         }
17433         
17434         while (dowCnt < this.weekStart + 7) {
17435             dow.cn.push({
17436                 tag: 'th',
17437                 cls: 'dow',
17438                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17439             });
17440         }
17441         
17442         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17443     },
17444     
17445     fillMonths: function()
17446     {    
17447         var i = 0;
17448         var months = this.picker().select('>.datepicker-months td', true).first();
17449         
17450         months.dom.innerHTML = '';
17451         
17452         while (i < 12) {
17453             var month = {
17454                 tag: 'span',
17455                 cls: 'month',
17456                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17457             };
17458             
17459             months.createChild(month);
17460         }
17461         
17462     },
17463     
17464     update: function()
17465     {
17466         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;
17467         
17468         if (this.date < this.startDate) {
17469             this.viewDate = new Date(this.startDate);
17470         } else if (this.date > this.endDate) {
17471             this.viewDate = new Date(this.endDate);
17472         } else {
17473             this.viewDate = new Date(this.date);
17474         }
17475         
17476         this.fill();
17477     },
17478     
17479     fill: function() 
17480     {
17481         var d = new Date(this.viewDate),
17482                 year = d.getUTCFullYear(),
17483                 month = d.getUTCMonth(),
17484                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17485                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17486                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17487                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17488                 currentDate = this.date && this.date.valueOf(),
17489                 today = this.UTCToday();
17490         
17491         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17492         
17493 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17494         
17495 //        this.picker.select('>tfoot th.today').
17496 //                                              .text(dates[this.language].today)
17497 //                                              .toggle(this.todayBtn !== false);
17498     
17499         this.updateNavArrows();
17500         this.fillMonths();
17501                                                 
17502         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17503         
17504         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17505          
17506         prevMonth.setUTCDate(day);
17507         
17508         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17509         
17510         var nextMonth = new Date(prevMonth);
17511         
17512         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17513         
17514         nextMonth = nextMonth.valueOf();
17515         
17516         var fillMonths = false;
17517         
17518         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17519         
17520         while(prevMonth.valueOf() < nextMonth) {
17521             var clsName = '';
17522             
17523             if (prevMonth.getUTCDay() === this.weekStart) {
17524                 if(fillMonths){
17525                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17526                 }
17527                     
17528                 fillMonths = {
17529                     tag: 'tr',
17530                     cn: []
17531                 };
17532                 
17533                 if(this.calendarWeeks){
17534                     // ISO 8601: First week contains first thursday.
17535                     // ISO also states week starts on Monday, but we can be more abstract here.
17536                     var
17537                     // Start of current week: based on weekstart/current date
17538                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17539                     // Thursday of this week
17540                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17541                     // First Thursday of year, year from thursday
17542                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17543                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17544                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17545                     
17546                     fillMonths.cn.push({
17547                         tag: 'td',
17548                         cls: 'cw',
17549                         html: calWeek
17550                     });
17551                 }
17552             }
17553             
17554             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17555                 clsName += ' old';
17556             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17557                 clsName += ' new';
17558             }
17559             if (this.todayHighlight &&
17560                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17561                 prevMonth.getUTCMonth() == today.getMonth() &&
17562                 prevMonth.getUTCDate() == today.getDate()) {
17563                 clsName += ' today';
17564             }
17565             
17566             if (currentDate && prevMonth.valueOf() === currentDate) {
17567                 clsName += ' active';
17568             }
17569             
17570             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17571                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17572                     clsName += ' disabled';
17573             }
17574             
17575             fillMonths.cn.push({
17576                 tag: 'td',
17577                 cls: 'day ' + clsName,
17578                 html: prevMonth.getDate()
17579             });
17580             
17581             prevMonth.setDate(prevMonth.getDate()+1);
17582         }
17583           
17584         var currentYear = this.date && this.date.getUTCFullYear();
17585         var currentMonth = this.date && this.date.getUTCMonth();
17586         
17587         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17588         
17589         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17590             v.removeClass('active');
17591             
17592             if(currentYear === year && k === currentMonth){
17593                 v.addClass('active');
17594             }
17595             
17596             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17597                 v.addClass('disabled');
17598             }
17599             
17600         });
17601         
17602         
17603         year = parseInt(year/10, 10) * 10;
17604         
17605         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17606         
17607         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17608         
17609         year -= 1;
17610         for (var i = -1; i < 11; i++) {
17611             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17612                 tag: 'span',
17613                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17614                 html: year
17615             });
17616             
17617             year += 1;
17618         }
17619     },
17620     
17621     showMode: function(dir) 
17622     {
17623         if (dir) {
17624             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17625         }
17626         
17627         Roo.each(this.picker().select('>div',true).elements, function(v){
17628             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17629             v.hide();
17630         });
17631         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17632     },
17633     
17634     place: function()
17635     {
17636         if(this.isInline) {
17637             return;
17638         }
17639         
17640         this.picker().removeClass(['bottom', 'top']);
17641         
17642         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17643             /*
17644              * place to the top of element!
17645              *
17646              */
17647             
17648             this.picker().addClass('top');
17649             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17650             
17651             return;
17652         }
17653         
17654         this.picker().addClass('bottom');
17655         
17656         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17657     },
17658     
17659     parseDate : function(value)
17660     {
17661         if(!value || value instanceof Date){
17662             return value;
17663         }
17664         var v = Date.parseDate(value, this.format);
17665         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17666             v = Date.parseDate(value, 'Y-m-d');
17667         }
17668         if(!v && this.altFormats){
17669             if(!this.altFormatsArray){
17670                 this.altFormatsArray = this.altFormats.split("|");
17671             }
17672             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17673                 v = Date.parseDate(value, this.altFormatsArray[i]);
17674             }
17675         }
17676         return v;
17677     },
17678     
17679     formatDate : function(date, fmt)
17680     {   
17681         return (!date || !(date instanceof Date)) ?
17682         date : date.dateFormat(fmt || this.format);
17683     },
17684     
17685     onFocus : function()
17686     {
17687         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17688         this.show();
17689     },
17690     
17691     onBlur : function()
17692     {
17693         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17694         
17695         var d = this.inputEl().getValue();
17696         
17697         this.setValue(d);
17698                 
17699         this.hide();
17700     },
17701     
17702     show : function()
17703     {
17704         this.picker().show();
17705         this.update();
17706         this.place();
17707         
17708         this.fireEvent('show', this, this.date);
17709     },
17710     
17711     hide : function()
17712     {
17713         if(this.isInline) {
17714             return;
17715         }
17716         this.picker().hide();
17717         this.viewMode = this.startViewMode;
17718         this.showMode();
17719         
17720         this.fireEvent('hide', this, this.date);
17721         
17722     },
17723     
17724     onMousedown: function(e)
17725     {
17726         e.stopPropagation();
17727         e.preventDefault();
17728     },
17729     
17730     keyup: function(e)
17731     {
17732         Roo.bootstrap.DateField.superclass.keyup.call(this);
17733         this.update();
17734     },
17735
17736     setValue: function(v)
17737     {
17738         if(this.fireEvent('beforeselect', this, v) !== false){
17739             var d = new Date(this.parseDate(v) ).clearTime();
17740         
17741             if(isNaN(d.getTime())){
17742                 this.date = this.viewDate = '';
17743                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17744                 return;
17745             }
17746
17747             v = this.formatDate(d);
17748
17749             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17750
17751             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17752
17753             this.update();
17754
17755             this.fireEvent('select', this, this.date);
17756         }
17757     },
17758     
17759     getValue: function()
17760     {
17761         return this.formatDate(this.date);
17762     },
17763     
17764     fireKey: function(e)
17765     {
17766         if (!this.picker().isVisible()){
17767             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17768                 this.show();
17769             }
17770             return;
17771         }
17772         
17773         var dateChanged = false,
17774         dir, day, month,
17775         newDate, newViewDate;
17776         
17777         switch(e.keyCode){
17778             case 27: // escape
17779                 this.hide();
17780                 e.preventDefault();
17781                 break;
17782             case 37: // left
17783             case 39: // right
17784                 if (!this.keyboardNavigation) {
17785                     break;
17786                 }
17787                 dir = e.keyCode == 37 ? -1 : 1;
17788                 
17789                 if (e.ctrlKey){
17790                     newDate = this.moveYear(this.date, dir);
17791                     newViewDate = this.moveYear(this.viewDate, dir);
17792                 } else if (e.shiftKey){
17793                     newDate = this.moveMonth(this.date, dir);
17794                     newViewDate = this.moveMonth(this.viewDate, dir);
17795                 } else {
17796                     newDate = new Date(this.date);
17797                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17798                     newViewDate = new Date(this.viewDate);
17799                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17800                 }
17801                 if (this.dateWithinRange(newDate)){
17802                     this.date = newDate;
17803                     this.viewDate = newViewDate;
17804                     this.setValue(this.formatDate(this.date));
17805 //                    this.update();
17806                     e.preventDefault();
17807                     dateChanged = true;
17808                 }
17809                 break;
17810             case 38: // up
17811             case 40: // down
17812                 if (!this.keyboardNavigation) {
17813                     break;
17814                 }
17815                 dir = e.keyCode == 38 ? -1 : 1;
17816                 if (e.ctrlKey){
17817                     newDate = this.moveYear(this.date, dir);
17818                     newViewDate = this.moveYear(this.viewDate, dir);
17819                 } else if (e.shiftKey){
17820                     newDate = this.moveMonth(this.date, dir);
17821                     newViewDate = this.moveMonth(this.viewDate, dir);
17822                 } else {
17823                     newDate = new Date(this.date);
17824                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17825                     newViewDate = new Date(this.viewDate);
17826                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17827                 }
17828                 if (this.dateWithinRange(newDate)){
17829                     this.date = newDate;
17830                     this.viewDate = newViewDate;
17831                     this.setValue(this.formatDate(this.date));
17832 //                    this.update();
17833                     e.preventDefault();
17834                     dateChanged = true;
17835                 }
17836                 break;
17837             case 13: // enter
17838                 this.setValue(this.formatDate(this.date));
17839                 this.hide();
17840                 e.preventDefault();
17841                 break;
17842             case 9: // tab
17843                 this.setValue(this.formatDate(this.date));
17844                 this.hide();
17845                 break;
17846             case 16: // shift
17847             case 17: // ctrl
17848             case 18: // alt
17849                 break;
17850             default :
17851                 this.hide();
17852                 
17853         }
17854     },
17855     
17856     
17857     onClick: function(e) 
17858     {
17859         e.stopPropagation();
17860         e.preventDefault();
17861         
17862         var target = e.getTarget();
17863         
17864         if(target.nodeName.toLowerCase() === 'i'){
17865             target = Roo.get(target).dom.parentNode;
17866         }
17867         
17868         var nodeName = target.nodeName;
17869         var className = target.className;
17870         var html = target.innerHTML;
17871         //Roo.log(nodeName);
17872         
17873         switch(nodeName.toLowerCase()) {
17874             case 'th':
17875                 switch(className) {
17876                     case 'switch':
17877                         this.showMode(1);
17878                         break;
17879                     case 'prev':
17880                     case 'next':
17881                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17882                         switch(this.viewMode){
17883                                 case 0:
17884                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17885                                         break;
17886                                 case 1:
17887                                 case 2:
17888                                         this.viewDate = this.moveYear(this.viewDate, dir);
17889                                         break;
17890                         }
17891                         this.fill();
17892                         break;
17893                     case 'today':
17894                         var date = new Date();
17895                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17896 //                        this.fill()
17897                         this.setValue(this.formatDate(this.date));
17898                         
17899                         this.hide();
17900                         break;
17901                 }
17902                 break;
17903             case 'span':
17904                 if (className.indexOf('disabled') < 0) {
17905                     this.viewDate.setUTCDate(1);
17906                     if (className.indexOf('month') > -1) {
17907                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17908                     } else {
17909                         var year = parseInt(html, 10) || 0;
17910                         this.viewDate.setUTCFullYear(year);
17911                         
17912                     }
17913                     
17914                     if(this.singleMode){
17915                         this.setValue(this.formatDate(this.viewDate));
17916                         this.hide();
17917                         return;
17918                     }
17919                     
17920                     this.showMode(-1);
17921                     this.fill();
17922                 }
17923                 break;
17924                 
17925             case 'td':
17926                 //Roo.log(className);
17927                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17928                     var day = parseInt(html, 10) || 1;
17929                     var year = this.viewDate.getUTCFullYear(),
17930                         month = this.viewDate.getUTCMonth();
17931
17932                     if (className.indexOf('old') > -1) {
17933                         if(month === 0 ){
17934                             month = 11;
17935                             year -= 1;
17936                         }else{
17937                             month -= 1;
17938                         }
17939                     } else if (className.indexOf('new') > -1) {
17940                         if (month == 11) {
17941                             month = 0;
17942                             year += 1;
17943                         } else {
17944                             month += 1;
17945                         }
17946                     }
17947                     //Roo.log([year,month,day]);
17948                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17949                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17950 //                    this.fill();
17951                     //Roo.log(this.formatDate(this.date));
17952                     this.setValue(this.formatDate(this.date));
17953                     this.hide();
17954                 }
17955                 break;
17956         }
17957     },
17958     
17959     setStartDate: function(startDate)
17960     {
17961         this.startDate = startDate || -Infinity;
17962         if (this.startDate !== -Infinity) {
17963             this.startDate = this.parseDate(this.startDate);
17964         }
17965         this.update();
17966         this.updateNavArrows();
17967     },
17968
17969     setEndDate: function(endDate)
17970     {
17971         this.endDate = endDate || Infinity;
17972         if (this.endDate !== Infinity) {
17973             this.endDate = this.parseDate(this.endDate);
17974         }
17975         this.update();
17976         this.updateNavArrows();
17977     },
17978     
17979     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17980     {
17981         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17982         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17983             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17984         }
17985         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17986             return parseInt(d, 10);
17987         });
17988         this.update();
17989         this.updateNavArrows();
17990     },
17991     
17992     updateNavArrows: function() 
17993     {
17994         if(this.singleMode){
17995             return;
17996         }
17997         
17998         var d = new Date(this.viewDate),
17999         year = d.getUTCFullYear(),
18000         month = d.getUTCMonth();
18001         
18002         Roo.each(this.picker().select('.prev', true).elements, function(v){
18003             v.show();
18004             switch (this.viewMode) {
18005                 case 0:
18006
18007                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18008                         v.hide();
18009                     }
18010                     break;
18011                 case 1:
18012                 case 2:
18013                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18014                         v.hide();
18015                     }
18016                     break;
18017             }
18018         });
18019         
18020         Roo.each(this.picker().select('.next', true).elements, function(v){
18021             v.show();
18022             switch (this.viewMode) {
18023                 case 0:
18024
18025                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18026                         v.hide();
18027                     }
18028                     break;
18029                 case 1:
18030                 case 2:
18031                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18032                         v.hide();
18033                     }
18034                     break;
18035             }
18036         })
18037     },
18038     
18039     moveMonth: function(date, dir)
18040     {
18041         if (!dir) {
18042             return date;
18043         }
18044         var new_date = new Date(date.valueOf()),
18045         day = new_date.getUTCDate(),
18046         month = new_date.getUTCMonth(),
18047         mag = Math.abs(dir),
18048         new_month, test;
18049         dir = dir > 0 ? 1 : -1;
18050         if (mag == 1){
18051             test = dir == -1
18052             // If going back one month, make sure month is not current month
18053             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18054             ? function(){
18055                 return new_date.getUTCMonth() == month;
18056             }
18057             // If going forward one month, make sure month is as expected
18058             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18059             : function(){
18060                 return new_date.getUTCMonth() != new_month;
18061             };
18062             new_month = month + dir;
18063             new_date.setUTCMonth(new_month);
18064             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18065             if (new_month < 0 || new_month > 11) {
18066                 new_month = (new_month + 12) % 12;
18067             }
18068         } else {
18069             // For magnitudes >1, move one month at a time...
18070             for (var i=0; i<mag; i++) {
18071                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18072                 new_date = this.moveMonth(new_date, dir);
18073             }
18074             // ...then reset the day, keeping it in the new month
18075             new_month = new_date.getUTCMonth();
18076             new_date.setUTCDate(day);
18077             test = function(){
18078                 return new_month != new_date.getUTCMonth();
18079             };
18080         }
18081         // Common date-resetting loop -- if date is beyond end of month, make it
18082         // end of month
18083         while (test()){
18084             new_date.setUTCDate(--day);
18085             new_date.setUTCMonth(new_month);
18086         }
18087         return new_date;
18088     },
18089
18090     moveYear: function(date, dir)
18091     {
18092         return this.moveMonth(date, dir*12);
18093     },
18094
18095     dateWithinRange: function(date)
18096     {
18097         return date >= this.startDate && date <= this.endDate;
18098     },
18099
18100     
18101     remove: function() 
18102     {
18103         this.picker().remove();
18104     }
18105    
18106 });
18107
18108 Roo.apply(Roo.bootstrap.DateField,  {
18109     
18110     head : {
18111         tag: 'thead',
18112         cn: [
18113         {
18114             tag: 'tr',
18115             cn: [
18116             {
18117                 tag: 'th',
18118                 cls: 'prev',
18119                 html: '<i class="fa fa-arrow-left"/>'
18120             },
18121             {
18122                 tag: 'th',
18123                 cls: 'switch',
18124                 colspan: '5'
18125             },
18126             {
18127                 tag: 'th',
18128                 cls: 'next',
18129                 html: '<i class="fa fa-arrow-right"/>'
18130             }
18131
18132             ]
18133         }
18134         ]
18135     },
18136     
18137     content : {
18138         tag: 'tbody',
18139         cn: [
18140         {
18141             tag: 'tr',
18142             cn: [
18143             {
18144                 tag: 'td',
18145                 colspan: '7'
18146             }
18147             ]
18148         }
18149         ]
18150     },
18151     
18152     footer : {
18153         tag: 'tfoot',
18154         cn: [
18155         {
18156             tag: 'tr',
18157             cn: [
18158             {
18159                 tag: 'th',
18160                 colspan: '7',
18161                 cls: 'today'
18162             }
18163                     
18164             ]
18165         }
18166         ]
18167     },
18168     
18169     dates:{
18170         en: {
18171             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18172             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18173             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18174             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18175             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18176             today: "Today"
18177         }
18178     },
18179     
18180     modes: [
18181     {
18182         clsName: 'days',
18183         navFnc: 'Month',
18184         navStep: 1
18185     },
18186     {
18187         clsName: 'months',
18188         navFnc: 'FullYear',
18189         navStep: 1
18190     },
18191     {
18192         clsName: 'years',
18193         navFnc: 'FullYear',
18194         navStep: 10
18195     }]
18196 });
18197
18198 Roo.apply(Roo.bootstrap.DateField,  {
18199   
18200     template : {
18201         tag: 'div',
18202         cls: 'datepicker dropdown-menu roo-dynamic',
18203         cn: [
18204         {
18205             tag: 'div',
18206             cls: 'datepicker-days',
18207             cn: [
18208             {
18209                 tag: 'table',
18210                 cls: 'table-condensed',
18211                 cn:[
18212                 Roo.bootstrap.DateField.head,
18213                 {
18214                     tag: 'tbody'
18215                 },
18216                 Roo.bootstrap.DateField.footer
18217                 ]
18218             }
18219             ]
18220         },
18221         {
18222             tag: 'div',
18223             cls: 'datepicker-months',
18224             cn: [
18225             {
18226                 tag: 'table',
18227                 cls: 'table-condensed',
18228                 cn:[
18229                 Roo.bootstrap.DateField.head,
18230                 Roo.bootstrap.DateField.content,
18231                 Roo.bootstrap.DateField.footer
18232                 ]
18233             }
18234             ]
18235         },
18236         {
18237             tag: 'div',
18238             cls: 'datepicker-years',
18239             cn: [
18240             {
18241                 tag: 'table',
18242                 cls: 'table-condensed',
18243                 cn:[
18244                 Roo.bootstrap.DateField.head,
18245                 Roo.bootstrap.DateField.content,
18246                 Roo.bootstrap.DateField.footer
18247                 ]
18248             }
18249             ]
18250         }
18251         ]
18252     }
18253 });
18254
18255  
18256
18257  /*
18258  * - LGPL
18259  *
18260  * TimeField
18261  * 
18262  */
18263
18264 /**
18265  * @class Roo.bootstrap.TimeField
18266  * @extends Roo.bootstrap.Input
18267  * Bootstrap DateField class
18268  * 
18269  * 
18270  * @constructor
18271  * Create a new TimeField
18272  * @param {Object} config The config object
18273  */
18274
18275 Roo.bootstrap.TimeField = function(config){
18276     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18277     this.addEvents({
18278             /**
18279              * @event show
18280              * Fires when this field show.
18281              * @param {Roo.bootstrap.DateField} thisthis
18282              * @param {Mixed} date The date value
18283              */
18284             show : true,
18285             /**
18286              * @event show
18287              * Fires when this field hide.
18288              * @param {Roo.bootstrap.DateField} this
18289              * @param {Mixed} date The date value
18290              */
18291             hide : true,
18292             /**
18293              * @event select
18294              * Fires when select a date.
18295              * @param {Roo.bootstrap.DateField} this
18296              * @param {Mixed} date The date value
18297              */
18298             select : true
18299         });
18300 };
18301
18302 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18303     
18304     /**
18305      * @cfg {String} format
18306      * The default time format string which can be overriden for localization support.  The format must be
18307      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18308      */
18309     format : "H:i",
18310        
18311     onRender: function(ct, position)
18312     {
18313         
18314         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18315                 
18316         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18317         
18318         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18319         
18320         this.pop = this.picker().select('>.datepicker-time',true).first();
18321         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18322         
18323         this.picker().on('mousedown', this.onMousedown, this);
18324         this.picker().on('click', this.onClick, this);
18325         
18326         this.picker().addClass('datepicker-dropdown');
18327     
18328         this.fillTime();
18329         this.update();
18330             
18331         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18332         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18333         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18334         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18335         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18336         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18337
18338     },
18339     
18340     fireKey: function(e){
18341         if (!this.picker().isVisible()){
18342             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18343                 this.show();
18344             }
18345             return;
18346         }
18347
18348         e.preventDefault();
18349         
18350         switch(e.keyCode){
18351             case 27: // escape
18352                 this.hide();
18353                 break;
18354             case 37: // left
18355             case 39: // right
18356                 this.onTogglePeriod();
18357                 break;
18358             case 38: // up
18359                 this.onIncrementMinutes();
18360                 break;
18361             case 40: // down
18362                 this.onDecrementMinutes();
18363                 break;
18364             case 13: // enter
18365             case 9: // tab
18366                 this.setTime();
18367                 break;
18368         }
18369     },
18370     
18371     onClick: function(e) {
18372         e.stopPropagation();
18373         e.preventDefault();
18374     },
18375     
18376     picker : function()
18377     {
18378         return this.el.select('.datepicker', true).first();
18379     },
18380     
18381     fillTime: function()
18382     {    
18383         var time = this.pop.select('tbody', true).first();
18384         
18385         time.dom.innerHTML = '';
18386         
18387         time.createChild({
18388             tag: 'tr',
18389             cn: [
18390                 {
18391                     tag: 'td',
18392                     cn: [
18393                         {
18394                             tag: 'a',
18395                             href: '#',
18396                             cls: 'btn',
18397                             cn: [
18398                                 {
18399                                     tag: 'span',
18400                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18401                                 }
18402                             ]
18403                         } 
18404                     ]
18405                 },
18406                 {
18407                     tag: 'td',
18408                     cls: 'separator'
18409                 },
18410                 {
18411                     tag: 'td',
18412                     cn: [
18413                         {
18414                             tag: 'a',
18415                             href: '#',
18416                             cls: 'btn',
18417                             cn: [
18418                                 {
18419                                     tag: 'span',
18420                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18421                                 }
18422                             ]
18423                         }
18424                     ]
18425                 },
18426                 {
18427                     tag: 'td',
18428                     cls: 'separator'
18429                 }
18430             ]
18431         });
18432         
18433         time.createChild({
18434             tag: 'tr',
18435             cn: [
18436                 {
18437                     tag: 'td',
18438                     cn: [
18439                         {
18440                             tag: 'span',
18441                             cls: 'timepicker-hour',
18442                             html: '00'
18443                         }  
18444                     ]
18445                 },
18446                 {
18447                     tag: 'td',
18448                     cls: 'separator',
18449                     html: ':'
18450                 },
18451                 {
18452                     tag: 'td',
18453                     cn: [
18454                         {
18455                             tag: 'span',
18456                             cls: 'timepicker-minute',
18457                             html: '00'
18458                         }  
18459                     ]
18460                 },
18461                 {
18462                     tag: 'td',
18463                     cls: 'separator'
18464                 },
18465                 {
18466                     tag: 'td',
18467                     cn: [
18468                         {
18469                             tag: 'button',
18470                             type: 'button',
18471                             cls: 'btn btn-primary period',
18472                             html: 'AM'
18473                             
18474                         }
18475                     ]
18476                 }
18477             ]
18478         });
18479         
18480         time.createChild({
18481             tag: 'tr',
18482             cn: [
18483                 {
18484                     tag: 'td',
18485                     cn: [
18486                         {
18487                             tag: 'a',
18488                             href: '#',
18489                             cls: 'btn',
18490                             cn: [
18491                                 {
18492                                     tag: 'span',
18493                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18494                                 }
18495                             ]
18496                         }
18497                     ]
18498                 },
18499                 {
18500                     tag: 'td',
18501                     cls: 'separator'
18502                 },
18503                 {
18504                     tag: 'td',
18505                     cn: [
18506                         {
18507                             tag: 'a',
18508                             href: '#',
18509                             cls: 'btn',
18510                             cn: [
18511                                 {
18512                                     tag: 'span',
18513                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18514                                 }
18515                             ]
18516                         }
18517                     ]
18518                 },
18519                 {
18520                     tag: 'td',
18521                     cls: 'separator'
18522                 }
18523             ]
18524         });
18525         
18526     },
18527     
18528     update: function()
18529     {
18530         
18531         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18532         
18533         this.fill();
18534     },
18535     
18536     fill: function() 
18537     {
18538         var hours = this.time.getHours();
18539         var minutes = this.time.getMinutes();
18540         var period = 'AM';
18541         
18542         if(hours > 11){
18543             period = 'PM';
18544         }
18545         
18546         if(hours == 0){
18547             hours = 12;
18548         }
18549         
18550         
18551         if(hours > 12){
18552             hours = hours - 12;
18553         }
18554         
18555         if(hours < 10){
18556             hours = '0' + hours;
18557         }
18558         
18559         if(minutes < 10){
18560             minutes = '0' + minutes;
18561         }
18562         
18563         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18564         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18565         this.pop.select('button', true).first().dom.innerHTML = period;
18566         
18567     },
18568     
18569     place: function()
18570     {   
18571         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18572         
18573         var cls = ['bottom'];
18574         
18575         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18576             cls.pop();
18577             cls.push('top');
18578         }
18579         
18580         cls.push('right');
18581         
18582         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18583             cls.pop();
18584             cls.push('left');
18585         }
18586         
18587         this.picker().addClass(cls.join('-'));
18588         
18589         var _this = this;
18590         
18591         Roo.each(cls, function(c){
18592             if(c == 'bottom'){
18593                 _this.picker().setTop(_this.inputEl().getHeight());
18594                 return;
18595             }
18596             if(c == 'top'){
18597                 _this.picker().setTop(0 - _this.picker().getHeight());
18598                 return;
18599             }
18600             
18601             if(c == 'left'){
18602                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18603                 return;
18604             }
18605             if(c == 'right'){
18606                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18607                 return;
18608             }
18609         });
18610         
18611     },
18612   
18613     onFocus : function()
18614     {
18615         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18616         this.show();
18617     },
18618     
18619     onBlur : function()
18620     {
18621         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18622         this.hide();
18623     },
18624     
18625     show : function()
18626     {
18627         this.picker().show();
18628         this.pop.show();
18629         this.update();
18630         this.place();
18631         
18632         this.fireEvent('show', this, this.date);
18633     },
18634     
18635     hide : function()
18636     {
18637         this.picker().hide();
18638         this.pop.hide();
18639         
18640         this.fireEvent('hide', this, this.date);
18641     },
18642     
18643     setTime : function()
18644     {
18645         this.hide();
18646         this.setValue(this.time.format(this.format));
18647         
18648         this.fireEvent('select', this, this.date);
18649         
18650         
18651     },
18652     
18653     onMousedown: function(e){
18654         e.stopPropagation();
18655         e.preventDefault();
18656     },
18657     
18658     onIncrementHours: function()
18659     {
18660         Roo.log('onIncrementHours');
18661         this.time = this.time.add(Date.HOUR, 1);
18662         this.update();
18663         
18664     },
18665     
18666     onDecrementHours: function()
18667     {
18668         Roo.log('onDecrementHours');
18669         this.time = this.time.add(Date.HOUR, -1);
18670         this.update();
18671     },
18672     
18673     onIncrementMinutes: function()
18674     {
18675         Roo.log('onIncrementMinutes');
18676         this.time = this.time.add(Date.MINUTE, 1);
18677         this.update();
18678     },
18679     
18680     onDecrementMinutes: function()
18681     {
18682         Roo.log('onDecrementMinutes');
18683         this.time = this.time.add(Date.MINUTE, -1);
18684         this.update();
18685     },
18686     
18687     onTogglePeriod: function()
18688     {
18689         Roo.log('onTogglePeriod');
18690         this.time = this.time.add(Date.HOUR, 12);
18691         this.update();
18692     }
18693     
18694    
18695 });
18696
18697 Roo.apply(Roo.bootstrap.TimeField,  {
18698     
18699     content : {
18700         tag: 'tbody',
18701         cn: [
18702             {
18703                 tag: 'tr',
18704                 cn: [
18705                 {
18706                     tag: 'td',
18707                     colspan: '7'
18708                 }
18709                 ]
18710             }
18711         ]
18712     },
18713     
18714     footer : {
18715         tag: 'tfoot',
18716         cn: [
18717             {
18718                 tag: 'tr',
18719                 cn: [
18720                 {
18721                     tag: 'th',
18722                     colspan: '7',
18723                     cls: '',
18724                     cn: [
18725                         {
18726                             tag: 'button',
18727                             cls: 'btn btn-info ok',
18728                             html: 'OK'
18729                         }
18730                     ]
18731                 }
18732
18733                 ]
18734             }
18735         ]
18736     }
18737 });
18738
18739 Roo.apply(Roo.bootstrap.TimeField,  {
18740   
18741     template : {
18742         tag: 'div',
18743         cls: 'datepicker dropdown-menu',
18744         cn: [
18745             {
18746                 tag: 'div',
18747                 cls: 'datepicker-time',
18748                 cn: [
18749                 {
18750                     tag: 'table',
18751                     cls: 'table-condensed',
18752                     cn:[
18753                     Roo.bootstrap.TimeField.content,
18754                     Roo.bootstrap.TimeField.footer
18755                     ]
18756                 }
18757                 ]
18758             }
18759         ]
18760     }
18761 });
18762
18763  
18764
18765  /*
18766  * - LGPL
18767  *
18768  * MonthField
18769  * 
18770  */
18771
18772 /**
18773  * @class Roo.bootstrap.MonthField
18774  * @extends Roo.bootstrap.Input
18775  * Bootstrap MonthField class
18776  * 
18777  * @cfg {String} language default en
18778  * 
18779  * @constructor
18780  * Create a new MonthField
18781  * @param {Object} config The config object
18782  */
18783
18784 Roo.bootstrap.MonthField = function(config){
18785     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18786     
18787     this.addEvents({
18788         /**
18789          * @event show
18790          * Fires when this field show.
18791          * @param {Roo.bootstrap.MonthField} this
18792          * @param {Mixed} date The date value
18793          */
18794         show : true,
18795         /**
18796          * @event show
18797          * Fires when this field hide.
18798          * @param {Roo.bootstrap.MonthField} this
18799          * @param {Mixed} date The date value
18800          */
18801         hide : true,
18802         /**
18803          * @event select
18804          * Fires when select a date.
18805          * @param {Roo.bootstrap.MonthField} this
18806          * @param {String} oldvalue The old value
18807          * @param {String} newvalue The new value
18808          */
18809         select : true
18810     });
18811 };
18812
18813 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18814     
18815     onRender: function(ct, position)
18816     {
18817         
18818         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18819         
18820         this.language = this.language || 'en';
18821         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18822         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18823         
18824         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18825         this.isInline = false;
18826         this.isInput = true;
18827         this.component = this.el.select('.add-on', true).first() || false;
18828         this.component = (this.component && this.component.length === 0) ? false : this.component;
18829         this.hasInput = this.component && this.inputEL().length;
18830         
18831         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18832         
18833         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18834         
18835         this.picker().on('mousedown', this.onMousedown, this);
18836         this.picker().on('click', this.onClick, this);
18837         
18838         this.picker().addClass('datepicker-dropdown');
18839         
18840         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18841             v.setStyle('width', '189px');
18842         });
18843         
18844         this.fillMonths();
18845         
18846         this.update();
18847         
18848         if(this.isInline) {
18849             this.show();
18850         }
18851         
18852     },
18853     
18854     setValue: function(v, suppressEvent)
18855     {   
18856         var o = this.getValue();
18857         
18858         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18859         
18860         this.update();
18861
18862         if(suppressEvent !== true){
18863             this.fireEvent('select', this, o, v);
18864         }
18865         
18866     },
18867     
18868     getValue: function()
18869     {
18870         return this.value;
18871     },
18872     
18873     onClick: function(e) 
18874     {
18875         e.stopPropagation();
18876         e.preventDefault();
18877         
18878         var target = e.getTarget();
18879         
18880         if(target.nodeName.toLowerCase() === 'i'){
18881             target = Roo.get(target).dom.parentNode;
18882         }
18883         
18884         var nodeName = target.nodeName;
18885         var className = target.className;
18886         var html = target.innerHTML;
18887         
18888         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18889             return;
18890         }
18891         
18892         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18893         
18894         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18895         
18896         this.hide();
18897                         
18898     },
18899     
18900     picker : function()
18901     {
18902         return this.pickerEl;
18903     },
18904     
18905     fillMonths: function()
18906     {    
18907         var i = 0;
18908         var months = this.picker().select('>.datepicker-months td', true).first();
18909         
18910         months.dom.innerHTML = '';
18911         
18912         while (i < 12) {
18913             var month = {
18914                 tag: 'span',
18915                 cls: 'month',
18916                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18917             };
18918             
18919             months.createChild(month);
18920         }
18921         
18922     },
18923     
18924     update: function()
18925     {
18926         var _this = this;
18927         
18928         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18929             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18930         }
18931         
18932         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18933             e.removeClass('active');
18934             
18935             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18936                 e.addClass('active');
18937             }
18938         })
18939     },
18940     
18941     place: function()
18942     {
18943         if(this.isInline) {
18944             return;
18945         }
18946         
18947         this.picker().removeClass(['bottom', 'top']);
18948         
18949         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18950             /*
18951              * place to the top of element!
18952              *
18953              */
18954             
18955             this.picker().addClass('top');
18956             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18957             
18958             return;
18959         }
18960         
18961         this.picker().addClass('bottom');
18962         
18963         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18964     },
18965     
18966     onFocus : function()
18967     {
18968         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18969         this.show();
18970     },
18971     
18972     onBlur : function()
18973     {
18974         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18975         
18976         var d = this.inputEl().getValue();
18977         
18978         this.setValue(d);
18979                 
18980         this.hide();
18981     },
18982     
18983     show : function()
18984     {
18985         this.picker().show();
18986         this.picker().select('>.datepicker-months', true).first().show();
18987         this.update();
18988         this.place();
18989         
18990         this.fireEvent('show', this, this.date);
18991     },
18992     
18993     hide : function()
18994     {
18995         if(this.isInline) {
18996             return;
18997         }
18998         this.picker().hide();
18999         this.fireEvent('hide', this, this.date);
19000         
19001     },
19002     
19003     onMousedown: function(e)
19004     {
19005         e.stopPropagation();
19006         e.preventDefault();
19007     },
19008     
19009     keyup: function(e)
19010     {
19011         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19012         this.update();
19013     },
19014
19015     fireKey: function(e)
19016     {
19017         if (!this.picker().isVisible()){
19018             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19019                 this.show();
19020             }
19021             return;
19022         }
19023         
19024         var dir;
19025         
19026         switch(e.keyCode){
19027             case 27: // escape
19028                 this.hide();
19029                 e.preventDefault();
19030                 break;
19031             case 37: // left
19032             case 39: // right
19033                 dir = e.keyCode == 37 ? -1 : 1;
19034                 
19035                 this.vIndex = this.vIndex + dir;
19036                 
19037                 if(this.vIndex < 0){
19038                     this.vIndex = 0;
19039                 }
19040                 
19041                 if(this.vIndex > 11){
19042                     this.vIndex = 11;
19043                 }
19044                 
19045                 if(isNaN(this.vIndex)){
19046                     this.vIndex = 0;
19047                 }
19048                 
19049                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19050                 
19051                 break;
19052             case 38: // up
19053             case 40: // down
19054                 
19055                 dir = e.keyCode == 38 ? -1 : 1;
19056                 
19057                 this.vIndex = this.vIndex + dir * 4;
19058                 
19059                 if(this.vIndex < 0){
19060                     this.vIndex = 0;
19061                 }
19062                 
19063                 if(this.vIndex > 11){
19064                     this.vIndex = 11;
19065                 }
19066                 
19067                 if(isNaN(this.vIndex)){
19068                     this.vIndex = 0;
19069                 }
19070                 
19071                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19072                 break;
19073                 
19074             case 13: // enter
19075                 
19076                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19077                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19078                 }
19079                 
19080                 this.hide();
19081                 e.preventDefault();
19082                 break;
19083             case 9: // tab
19084                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19085                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19086                 }
19087                 this.hide();
19088                 break;
19089             case 16: // shift
19090             case 17: // ctrl
19091             case 18: // alt
19092                 break;
19093             default :
19094                 this.hide();
19095                 
19096         }
19097     },
19098     
19099     remove: function() 
19100     {
19101         this.picker().remove();
19102     }
19103    
19104 });
19105
19106 Roo.apply(Roo.bootstrap.MonthField,  {
19107     
19108     content : {
19109         tag: 'tbody',
19110         cn: [
19111         {
19112             tag: 'tr',
19113             cn: [
19114             {
19115                 tag: 'td',
19116                 colspan: '7'
19117             }
19118             ]
19119         }
19120         ]
19121     },
19122     
19123     dates:{
19124         en: {
19125             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19126             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19127         }
19128     }
19129 });
19130
19131 Roo.apply(Roo.bootstrap.MonthField,  {
19132   
19133     template : {
19134         tag: 'div',
19135         cls: 'datepicker dropdown-menu roo-dynamic',
19136         cn: [
19137             {
19138                 tag: 'div',
19139                 cls: 'datepicker-months',
19140                 cn: [
19141                 {
19142                     tag: 'table',
19143                     cls: 'table-condensed',
19144                     cn:[
19145                         Roo.bootstrap.DateField.content
19146                     ]
19147                 }
19148                 ]
19149             }
19150         ]
19151     }
19152 });
19153
19154  
19155
19156  
19157  /*
19158  * - LGPL
19159  *
19160  * CheckBox
19161  * 
19162  */
19163
19164 /**
19165  * @class Roo.bootstrap.CheckBox
19166  * @extends Roo.bootstrap.Input
19167  * Bootstrap CheckBox class
19168  * 
19169  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19170  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19171  * @cfg {String} boxLabel The text that appears beside the checkbox
19172  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19173  * @cfg {Boolean} checked initnal the element
19174  * @cfg {Boolean} inline inline the element (default false)
19175  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19176  * 
19177  * @constructor
19178  * Create a new CheckBox
19179  * @param {Object} config The config object
19180  */
19181
19182 Roo.bootstrap.CheckBox = function(config){
19183     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19184    
19185     this.addEvents({
19186         /**
19187         * @event check
19188         * Fires when the element is checked or unchecked.
19189         * @param {Roo.bootstrap.CheckBox} this This input
19190         * @param {Boolean} checked The new checked value
19191         */
19192        check : true
19193     });
19194     
19195 };
19196
19197 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19198   
19199     inputType: 'checkbox',
19200     inputValue: 1,
19201     valueOff: 0,
19202     boxLabel: false,
19203     checked: false,
19204     weight : false,
19205     inline: false,
19206     
19207     getAutoCreate : function()
19208     {
19209         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19210         
19211         var id = Roo.id();
19212         
19213         var cfg = {};
19214         
19215         cfg.cls = 'form-group ' + this.inputType; //input-group
19216         
19217         if(this.inline){
19218             cfg.cls += ' ' + this.inputType + '-inline';
19219         }
19220         
19221         var input =  {
19222             tag: 'input',
19223             id : id,
19224             type : this.inputType,
19225             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19226             cls : 'roo-' + this.inputType, //'form-box',
19227             placeholder : this.placeholder || ''
19228             
19229         };
19230         
19231         if (this.weight) { // Validity check?
19232             cfg.cls += " " + this.inputType + "-" + this.weight;
19233         }
19234         
19235         if (this.disabled) {
19236             input.disabled=true;
19237         }
19238         
19239         if(this.checked){
19240             input.checked = this.checked;
19241         }
19242         
19243         if (this.name) {
19244             input.name = this.name;
19245         }
19246         
19247         if (this.size) {
19248             input.cls += ' input-' + this.size;
19249         }
19250         
19251         var settings=this;
19252         
19253         ['xs','sm','md','lg'].map(function(size){
19254             if (settings[size]) {
19255                 cfg.cls += ' col-' + size + '-' + settings[size];
19256             }
19257         });
19258         
19259         var inputblock = input;
19260          
19261         if (this.before || this.after) {
19262             
19263             inputblock = {
19264                 cls : 'input-group',
19265                 cn :  [] 
19266             };
19267             
19268             if (this.before) {
19269                 inputblock.cn.push({
19270                     tag :'span',
19271                     cls : 'input-group-addon',
19272                     html : this.before
19273                 });
19274             }
19275             
19276             inputblock.cn.push(input);
19277             
19278             if (this.after) {
19279                 inputblock.cn.push({
19280                     tag :'span',
19281                     cls : 'input-group-addon',
19282                     html : this.after
19283                 });
19284             }
19285             
19286         }
19287         
19288         if (align ==='left' && this.fieldLabel.length) {
19289 //                Roo.log("left and has label");
19290                 cfg.cn = [
19291                     
19292                     {
19293                         tag: 'label',
19294                         'for' :  id,
19295                         cls : 'control-label col-md-' + this.labelWidth,
19296                         html : this.fieldLabel
19297                         
19298                     },
19299                     {
19300                         cls : "col-md-" + (12 - this.labelWidth), 
19301                         cn: [
19302                             inputblock
19303                         ]
19304                     }
19305                     
19306                 ];
19307         } else if ( this.fieldLabel.length) {
19308 //                Roo.log(" label");
19309                 cfg.cn = [
19310                    
19311                     {
19312                         tag: this.boxLabel ? 'span' : 'label',
19313                         'for': id,
19314                         cls: 'control-label box-input-label',
19315                         //cls : 'input-group-addon',
19316                         html : this.fieldLabel
19317                         
19318                     },
19319                     
19320                     inputblock
19321                     
19322                 ];
19323
19324         } else {
19325             
19326 //                Roo.log(" no label && no align");
19327                 cfg.cn = [  inputblock ] ;
19328                 
19329                 
19330         }
19331         
19332         if(this.boxLabel){
19333              var boxLabelCfg = {
19334                 tag: 'label',
19335                 //'for': id, // box label is handled by onclick - so no for...
19336                 cls: 'box-label',
19337                 html: this.boxLabel
19338             };
19339             
19340             if(this.tooltip){
19341                 boxLabelCfg.tooltip = this.tooltip;
19342             }
19343              
19344             cfg.cn.push(boxLabelCfg);
19345         }
19346         
19347         
19348        
19349         return cfg;
19350         
19351     },
19352     
19353     /**
19354      * return the real input element.
19355      */
19356     inputEl: function ()
19357     {
19358         return this.el.select('input.roo-' + this.inputType,true).first();
19359     },
19360     
19361     labelEl: function()
19362     {
19363         return this.el.select('label.control-label',true).first();
19364     },
19365     /* depricated... */
19366     
19367     label: function()
19368     {
19369         return this.labelEl();
19370     },
19371     
19372     boxLabelEl: function()
19373     {
19374         return this.el.select('label.box-label',true).first();
19375     },
19376     
19377     initEvents : function()
19378     {
19379 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19380         
19381         this.inputEl().on('click', this.onClick,  this);
19382         
19383         if (this.boxLabel) { 
19384             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19385         }
19386         
19387         this.startValue = this.getValue();
19388         
19389         if(this.groupId){
19390             Roo.bootstrap.CheckBox.register(this);
19391         }
19392     },
19393     
19394     onClick : function()
19395     {   
19396         this.setChecked(!this.checked);
19397     },
19398     
19399     setChecked : function(state,suppressEvent)
19400     {
19401         this.startValue = this.getValue();
19402         
19403         if(this.inputType == 'radio'){
19404             
19405             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19406                 e.dom.checked = false;
19407             });
19408             
19409             this.inputEl().dom.checked = true;
19410             
19411             this.inputEl().dom.value = this.inputValue;
19412             
19413             if(suppressEvent !== true){
19414                 this.fireEvent('check', this, true);
19415             }
19416             
19417             this.validate();
19418             
19419             return;
19420         }
19421         
19422         this.checked = state;
19423         
19424         this.inputEl().dom.checked = state;
19425         
19426         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19427         
19428         if(suppressEvent !== true){
19429             this.fireEvent('check', this, state);
19430         }
19431         
19432         this.validate();
19433     },
19434     
19435     getValue : function()
19436     {
19437         if(this.inputType == 'radio'){
19438             return this.getGroupValue();
19439         }
19440         
19441         return this.inputEl().getValue();
19442         
19443     },
19444     
19445     getGroupValue : function()
19446     {
19447         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19448             return '';
19449         }
19450         
19451         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19452     },
19453     
19454     setValue : function(v,suppressEvent)
19455     {
19456         if(this.inputType == 'radio'){
19457             this.setGroupValue(v, suppressEvent);
19458             return;
19459         }
19460         
19461         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19462         
19463         this.validate();
19464     },
19465     
19466     setGroupValue : function(v, suppressEvent)
19467     {
19468         this.startValue = this.getValue();
19469         
19470         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19471             e.dom.checked = false;
19472             
19473             if(e.dom.value == v){
19474                 e.dom.checked = true;
19475             }
19476         });
19477         
19478         if(suppressEvent !== true){
19479             this.fireEvent('check', this, true);
19480         }
19481
19482         this.validate();
19483         
19484         return;
19485     },
19486     
19487     validate : function()
19488     {
19489         if(
19490                 this.disabled || 
19491                 (this.inputType == 'radio' && this.validateRadio()) ||
19492                 (this.inputType == 'checkbox' && this.validateCheckbox())
19493         ){
19494             this.markValid();
19495             return true;
19496         }
19497         
19498         this.markInvalid();
19499         return false;
19500     },
19501     
19502     validateRadio : function()
19503     {
19504         var valid = false;
19505         
19506         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19507             if(!e.dom.checked){
19508                 return;
19509             }
19510             
19511             valid = true;
19512             
19513             return false;
19514         });
19515         
19516         return valid;
19517     },
19518     
19519     validateCheckbox : function()
19520     {
19521         if(!this.groupId){
19522             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19523         }
19524         
19525         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19526         
19527         if(!group){
19528             return false;
19529         }
19530         
19531         var r = false;
19532         
19533         for(var i in group){
19534             if(r){
19535                 break;
19536             }
19537             
19538             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19539         }
19540         
19541         return r;
19542     },
19543     
19544     /**
19545      * Mark this field as valid
19546      */
19547     markValid : function()
19548     {
19549         if(this.allowBlank){
19550             return;
19551         }
19552         
19553         var _this = this;
19554         
19555         this.fireEvent('valid', this);
19556         
19557         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19558         
19559         if(this.groupId){
19560             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19561         }
19562         
19563         if(label){
19564             label.markValid();
19565         }
19566         
19567         if(this.inputType == 'radio'){
19568             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19569                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19570                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19571             });
19572             
19573             return;
19574         }
19575         
19576         if(!this.groupId){
19577             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19578             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19579             return;
19580         }
19581         
19582         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19583             
19584         if(!group){
19585             return;
19586         }
19587         
19588         for(var i in group){
19589             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19590             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19591         }
19592     },
19593     
19594      /**
19595      * Mark this field as invalid
19596      * @param {String} msg The validation message
19597      */
19598     markInvalid : function(msg)
19599     {
19600         if(this.allowBlank){
19601             return;
19602         }
19603         
19604         var _this = this;
19605         
19606         this.fireEvent('invalid', this, msg);
19607         
19608         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19609         
19610         if(this.groupId){
19611             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19612         }
19613         
19614         if(label){
19615             label.markInvalid();
19616         }
19617             
19618         if(this.inputType == 'radio'){
19619             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19620                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19621                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19622             });
19623             
19624             return;
19625         }
19626         
19627         if(!this.groupId){
19628             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19629             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19630             return;
19631         }
19632         
19633         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19634         
19635         if(!group){
19636             return;
19637         }
19638         
19639         for(var i in group){
19640             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19641             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19642         }
19643         
19644     }
19645     
19646 });
19647
19648 Roo.apply(Roo.bootstrap.CheckBox, {
19649     
19650     groups: {},
19651     
19652      /**
19653     * register a CheckBox Group
19654     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19655     */
19656     register : function(checkbox)
19657     {
19658         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19659             this.groups[checkbox.groupId] = {};
19660         }
19661         
19662         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19663             return;
19664         }
19665         
19666         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19667         
19668     },
19669     /**
19670     * fetch a CheckBox Group based on the group ID
19671     * @param {string} the group ID
19672     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19673     */
19674     get: function(groupId) {
19675         if (typeof(this.groups[groupId]) == 'undefined') {
19676             return false;
19677         }
19678         
19679         return this.groups[groupId] ;
19680     }
19681     
19682     
19683 });
19684 /*
19685  * - LGPL
19686  *
19687  * Radio
19688  *
19689  *
19690  * not inline
19691  *<div class="radio">
19692   <label>
19693     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19694     Option one is this and that&mdash;be sure to include why it's great
19695   </label>
19696 </div>
19697  *
19698  *
19699  *inline
19700  *<span>
19701  *<label class="radio-inline">fieldLabel</label>
19702  *<label class="radio-inline">
19703   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19704 </label>
19705 <span>
19706  * 
19707  * 
19708  */
19709
19710 /**
19711  * @class Roo.bootstrap.Radio
19712  * @extends Roo.bootstrap.CheckBox
19713  * Bootstrap Radio class
19714
19715  * @constructor
19716  * Create a new Radio
19717  * @param {Object} config The config object
19718  */
19719
19720 Roo.bootstrap.Radio = function(config){
19721     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19722    
19723 };
19724
19725 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19726     
19727     inputType: 'radio',
19728     inputValue: '',
19729     valueOff: '',
19730     
19731     getAutoCreate : function()
19732     {
19733         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19734         align = align || 'left'; // default...
19735         
19736         
19737         
19738         var id = Roo.id();
19739         
19740         var cfg = {
19741                 tag : this.inline ? 'span' : 'div',
19742                 cls : '',
19743                 cn : []
19744         };
19745         
19746         var inline = this.inline ? ' radio-inline' : '';
19747         
19748         var lbl = {
19749                 tag: 'label' ,
19750                 // does not need for, as we wrap the input with it..
19751                 'for' : id,
19752                 cls : 'control-label box-label' + inline,
19753                 cn : []
19754         };
19755         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19756         
19757         var fieldLabel = {
19758             tag: 'label' ,
19759             //cls : 'control-label' + inline,
19760             html : this.fieldLabel,
19761             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19762         };
19763         
19764  
19765         
19766         
19767         var input =  {
19768             tag: 'input',
19769             id : id,
19770             type : this.inputType,
19771             //value : (!this.checked) ? this.valueOff : this.inputValue,
19772             value : this.inputValue,
19773             cls : 'roo-radio',
19774             placeholder : this.placeholder || '' // ?? needed????
19775             
19776         };
19777         if (this.weight) { // Validity check?
19778             input.cls += " radio-" + this.weight;
19779         }
19780         if (this.disabled) {
19781             input.disabled=true;
19782         }
19783         
19784         if(this.checked){
19785             input.checked = this.checked;
19786         }
19787         
19788         if (this.name) {
19789             input.name = this.name;
19790         }
19791         
19792         if (this.size) {
19793             input.cls += ' input-' + this.size;
19794         }
19795         
19796         //?? can span's inline have a width??
19797         
19798         var settings=this;
19799         ['xs','sm','md','lg'].map(function(size){
19800             if (settings[size]) {
19801                 cfg.cls += ' col-' + size + '-' + settings[size];
19802             }
19803         });
19804         
19805         var inputblock = input;
19806         
19807         if (this.before || this.after) {
19808             
19809             inputblock = {
19810                 cls : 'input-group',
19811                 tag : 'span',
19812                 cn :  [] 
19813             };
19814             if (this.before) {
19815                 inputblock.cn.push({
19816                     tag :'span',
19817                     cls : 'input-group-addon',
19818                     html : this.before
19819                 });
19820             }
19821             inputblock.cn.push(input);
19822             if (this.after) {
19823                 inputblock.cn.push({
19824                     tag :'span',
19825                     cls : 'input-group-addon',
19826                     html : this.after
19827                 });
19828             }
19829             
19830         };
19831         
19832         
19833         if (this.fieldLabel && this.fieldLabel.length) {
19834             cfg.cn.push(fieldLabel);
19835         }
19836        
19837         // normal bootstrap puts the input inside the label.
19838         // however with our styled version - it has to go after the input.
19839        
19840         //lbl.cn.push(inputblock);
19841         
19842         var lblwrap =  {
19843             tag: 'span',
19844             cls: 'radio' + inline,
19845             cn: [
19846                 inputblock,
19847                 lbl
19848             ]
19849         };
19850         
19851         cfg.cn.push( lblwrap);
19852         
19853         if(this.boxLabel){
19854             lbl.cn.push({
19855                 tag: 'span',
19856                 html: this.boxLabel
19857             })
19858         }
19859          
19860         
19861         return cfg;
19862         
19863     },
19864     
19865     initEvents : function()
19866     {
19867 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19868         
19869         this.inputEl().on('click', this.onClick,  this);
19870         if (this.boxLabel) {
19871             //Roo.log('find label');
19872             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19873         }
19874         
19875     },
19876     
19877     inputEl: function ()
19878     {
19879         return this.el.select('input.roo-radio',true).first();
19880     },
19881     onClick : function()
19882     {   
19883         Roo.log("click");
19884         this.setChecked(true);
19885     },
19886     
19887     setChecked : function(state,suppressEvent)
19888     {
19889         if(state){
19890             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19891                 v.dom.checked = false;
19892             });
19893         }
19894         Roo.log(this.inputEl().dom);
19895         this.checked = state;
19896         this.inputEl().dom.checked = state;
19897         
19898         if(suppressEvent !== true){
19899             this.fireEvent('check', this, state);
19900         }
19901         
19902         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19903         
19904     },
19905     
19906     getGroupValue : function()
19907     {
19908         var value = '';
19909         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19910             if(v.dom.checked == true){
19911                 value = v.dom.value;
19912             }
19913         });
19914         
19915         return value;
19916     },
19917     
19918     /**
19919      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19920      * @return {Mixed} value The field value
19921      */
19922     getValue : function(){
19923         return this.getGroupValue();
19924     }
19925     
19926 });
19927
19928  
19929 //<script type="text/javascript">
19930
19931 /*
19932  * Based  Ext JS Library 1.1.1
19933  * Copyright(c) 2006-2007, Ext JS, LLC.
19934  * LGPL
19935  *
19936  */
19937  
19938 /**
19939  * @class Roo.HtmlEditorCore
19940  * @extends Roo.Component
19941  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19942  *
19943  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19944  */
19945
19946 Roo.HtmlEditorCore = function(config){
19947     
19948     
19949     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19950     
19951     
19952     this.addEvents({
19953         /**
19954          * @event initialize
19955          * Fires when the editor is fully initialized (including the iframe)
19956          * @param {Roo.HtmlEditorCore} this
19957          */
19958         initialize: true,
19959         /**
19960          * @event activate
19961          * Fires when the editor is first receives the focus. Any insertion must wait
19962          * until after this event.
19963          * @param {Roo.HtmlEditorCore} this
19964          */
19965         activate: true,
19966          /**
19967          * @event beforesync
19968          * Fires before the textarea is updated with content from the editor iframe. Return false
19969          * to cancel the sync.
19970          * @param {Roo.HtmlEditorCore} this
19971          * @param {String} html
19972          */
19973         beforesync: true,
19974          /**
19975          * @event beforepush
19976          * Fires before the iframe editor is updated with content from the textarea. Return false
19977          * to cancel the push.
19978          * @param {Roo.HtmlEditorCore} this
19979          * @param {String} html
19980          */
19981         beforepush: true,
19982          /**
19983          * @event sync
19984          * Fires when the textarea is updated with content from the editor iframe.
19985          * @param {Roo.HtmlEditorCore} this
19986          * @param {String} html
19987          */
19988         sync: true,
19989          /**
19990          * @event push
19991          * Fires when the iframe editor is updated with content from the textarea.
19992          * @param {Roo.HtmlEditorCore} this
19993          * @param {String} html
19994          */
19995         push: true,
19996         
19997         /**
19998          * @event editorevent
19999          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20000          * @param {Roo.HtmlEditorCore} this
20001          */
20002         editorevent: true
20003         
20004     });
20005     
20006     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20007     
20008     // defaults : white / black...
20009     this.applyBlacklists();
20010     
20011     
20012     
20013 };
20014
20015
20016 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20017
20018
20019      /**
20020      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20021      */
20022     
20023     owner : false,
20024     
20025      /**
20026      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20027      *                        Roo.resizable.
20028      */
20029     resizable : false,
20030      /**
20031      * @cfg {Number} height (in pixels)
20032      */   
20033     height: 300,
20034    /**
20035      * @cfg {Number} width (in pixels)
20036      */   
20037     width: 500,
20038     
20039     /**
20040      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20041      * 
20042      */
20043     stylesheets: false,
20044     
20045     // id of frame..
20046     frameId: false,
20047     
20048     // private properties
20049     validationEvent : false,
20050     deferHeight: true,
20051     initialized : false,
20052     activated : false,
20053     sourceEditMode : false,
20054     onFocus : Roo.emptyFn,
20055     iframePad:3,
20056     hideMode:'offsets',
20057     
20058     clearUp: true,
20059     
20060     // blacklist + whitelisted elements..
20061     black: false,
20062     white: false,
20063      
20064     
20065
20066     /**
20067      * Protected method that will not generally be called directly. It
20068      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20069      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20070      */
20071     getDocMarkup : function(){
20072         // body styles..
20073         var st = '';
20074         
20075         // inherit styels from page...?? 
20076         if (this.stylesheets === false) {
20077             
20078             Roo.get(document.head).select('style').each(function(node) {
20079                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20080             });
20081             
20082             Roo.get(document.head).select('link').each(function(node) { 
20083                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20084             });
20085             
20086         } else if (!this.stylesheets.length) {
20087                 // simple..
20088                 st = '<style type="text/css">' +
20089                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20090                    '</style>';
20091         } else { 
20092             
20093         }
20094         
20095         st +=  '<style type="text/css">' +
20096             'IMG { cursor: pointer } ' +
20097         '</style>';
20098
20099         
20100         return '<html><head>' + st  +
20101             //<style type="text/css">' +
20102             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20103             //'</style>' +
20104             ' </head><body class="roo-htmleditor-body"></body></html>';
20105     },
20106
20107     // private
20108     onRender : function(ct, position)
20109     {
20110         var _t = this;
20111         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20112         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20113         
20114         
20115         this.el.dom.style.border = '0 none';
20116         this.el.dom.setAttribute('tabIndex', -1);
20117         this.el.addClass('x-hidden hide');
20118         
20119         
20120         
20121         if(Roo.isIE){ // fix IE 1px bogus margin
20122             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20123         }
20124        
20125         
20126         this.frameId = Roo.id();
20127         
20128          
20129         
20130         var iframe = this.owner.wrap.createChild({
20131             tag: 'iframe',
20132             cls: 'form-control', // bootstrap..
20133             id: this.frameId,
20134             name: this.frameId,
20135             frameBorder : 'no',
20136             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20137         }, this.el
20138         );
20139         
20140         
20141         this.iframe = iframe.dom;
20142
20143          this.assignDocWin();
20144         
20145         this.doc.designMode = 'on';
20146        
20147         this.doc.open();
20148         this.doc.write(this.getDocMarkup());
20149         this.doc.close();
20150
20151         
20152         var task = { // must defer to wait for browser to be ready
20153             run : function(){
20154                 //console.log("run task?" + this.doc.readyState);
20155                 this.assignDocWin();
20156                 if(this.doc.body || this.doc.readyState == 'complete'){
20157                     try {
20158                         this.doc.designMode="on";
20159                     } catch (e) {
20160                         return;
20161                     }
20162                     Roo.TaskMgr.stop(task);
20163                     this.initEditor.defer(10, this);
20164                 }
20165             },
20166             interval : 10,
20167             duration: 10000,
20168             scope: this
20169         };
20170         Roo.TaskMgr.start(task);
20171
20172     },
20173
20174     // private
20175     onResize : function(w, h)
20176     {
20177          Roo.log('resize: ' +w + ',' + h );
20178         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20179         if(!this.iframe){
20180             return;
20181         }
20182         if(typeof w == 'number'){
20183             
20184             this.iframe.style.width = w + 'px';
20185         }
20186         if(typeof h == 'number'){
20187             
20188             this.iframe.style.height = h + 'px';
20189             if(this.doc){
20190                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20191             }
20192         }
20193         
20194     },
20195
20196     /**
20197      * Toggles the editor between standard and source edit mode.
20198      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20199      */
20200     toggleSourceEdit : function(sourceEditMode){
20201         
20202         this.sourceEditMode = sourceEditMode === true;
20203         
20204         if(this.sourceEditMode){
20205  
20206             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20207             
20208         }else{
20209             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20210             //this.iframe.className = '';
20211             this.deferFocus();
20212         }
20213         //this.setSize(this.owner.wrap.getSize());
20214         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20215     },
20216
20217     
20218   
20219
20220     /**
20221      * Protected method that will not generally be called directly. If you need/want
20222      * custom HTML cleanup, this is the method you should override.
20223      * @param {String} html The HTML to be cleaned
20224      * return {String} The cleaned HTML
20225      */
20226     cleanHtml : function(html){
20227         html = String(html);
20228         if(html.length > 5){
20229             if(Roo.isSafari){ // strip safari nonsense
20230                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20231             }
20232         }
20233         if(html == '&nbsp;'){
20234             html = '';
20235         }
20236         return html;
20237     },
20238
20239     /**
20240      * HTML Editor -> Textarea
20241      * Protected method that will not generally be called directly. Syncs the contents
20242      * of the editor iframe with the textarea.
20243      */
20244     syncValue : function(){
20245         if(this.initialized){
20246             var bd = (this.doc.body || this.doc.documentElement);
20247             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20248             var html = bd.innerHTML;
20249             if(Roo.isSafari){
20250                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20251                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20252                 if(m && m[1]){
20253                     html = '<div style="'+m[0]+'">' + html + '</div>';
20254                 }
20255             }
20256             html = this.cleanHtml(html);
20257             // fix up the special chars.. normaly like back quotes in word...
20258             // however we do not want to do this with chinese..
20259             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20260                 var cc = b.charCodeAt();
20261                 if (
20262                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20263                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20264                     (cc >= 0xf900 && cc < 0xfb00 )
20265                 ) {
20266                         return b;
20267                 }
20268                 return "&#"+cc+";" 
20269             });
20270             if(this.owner.fireEvent('beforesync', this, html) !== false){
20271                 this.el.dom.value = html;
20272                 this.owner.fireEvent('sync', this, html);
20273             }
20274         }
20275     },
20276
20277     /**
20278      * Protected method that will not generally be called directly. Pushes the value of the textarea
20279      * into the iframe editor.
20280      */
20281     pushValue : function(){
20282         if(this.initialized){
20283             var v = this.el.dom.value.trim();
20284             
20285 //            if(v.length < 1){
20286 //                v = '&#160;';
20287 //            }
20288             
20289             if(this.owner.fireEvent('beforepush', this, v) !== false){
20290                 var d = (this.doc.body || this.doc.documentElement);
20291                 d.innerHTML = v;
20292                 this.cleanUpPaste();
20293                 this.el.dom.value = d.innerHTML;
20294                 this.owner.fireEvent('push', this, v);
20295             }
20296         }
20297     },
20298
20299     // private
20300     deferFocus : function(){
20301         this.focus.defer(10, this);
20302     },
20303
20304     // doc'ed in Field
20305     focus : function(){
20306         if(this.win && !this.sourceEditMode){
20307             this.win.focus();
20308         }else{
20309             this.el.focus();
20310         }
20311     },
20312     
20313     assignDocWin: function()
20314     {
20315         var iframe = this.iframe;
20316         
20317          if(Roo.isIE){
20318             this.doc = iframe.contentWindow.document;
20319             this.win = iframe.contentWindow;
20320         } else {
20321 //            if (!Roo.get(this.frameId)) {
20322 //                return;
20323 //            }
20324 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20325 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20326             
20327             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20328                 return;
20329             }
20330             
20331             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20332             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20333         }
20334     },
20335     
20336     // private
20337     initEditor : function(){
20338         //console.log("INIT EDITOR");
20339         this.assignDocWin();
20340         
20341         
20342         
20343         this.doc.designMode="on";
20344         this.doc.open();
20345         this.doc.write(this.getDocMarkup());
20346         this.doc.close();
20347         
20348         var dbody = (this.doc.body || this.doc.documentElement);
20349         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20350         // this copies styles from the containing element into thsi one..
20351         // not sure why we need all of this..
20352         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20353         
20354         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20355         //ss['background-attachment'] = 'fixed'; // w3c
20356         dbody.bgProperties = 'fixed'; // ie
20357         //Roo.DomHelper.applyStyles(dbody, ss);
20358         Roo.EventManager.on(this.doc, {
20359             //'mousedown': this.onEditorEvent,
20360             'mouseup': this.onEditorEvent,
20361             'dblclick': this.onEditorEvent,
20362             'click': this.onEditorEvent,
20363             'keyup': this.onEditorEvent,
20364             buffer:100,
20365             scope: this
20366         });
20367         if(Roo.isGecko){
20368             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20369         }
20370         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20371             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20372         }
20373         this.initialized = true;
20374
20375         this.owner.fireEvent('initialize', this);
20376         this.pushValue();
20377     },
20378
20379     // private
20380     onDestroy : function(){
20381         
20382         
20383         
20384         if(this.rendered){
20385             
20386             //for (var i =0; i < this.toolbars.length;i++) {
20387             //    // fixme - ask toolbars for heights?
20388             //    this.toolbars[i].onDestroy();
20389            // }
20390             
20391             //this.wrap.dom.innerHTML = '';
20392             //this.wrap.remove();
20393         }
20394     },
20395
20396     // private
20397     onFirstFocus : function(){
20398         
20399         this.assignDocWin();
20400         
20401         
20402         this.activated = true;
20403          
20404     
20405         if(Roo.isGecko){ // prevent silly gecko errors
20406             this.win.focus();
20407             var s = this.win.getSelection();
20408             if(!s.focusNode || s.focusNode.nodeType != 3){
20409                 var r = s.getRangeAt(0);
20410                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20411                 r.collapse(true);
20412                 this.deferFocus();
20413             }
20414             try{
20415                 this.execCmd('useCSS', true);
20416                 this.execCmd('styleWithCSS', false);
20417             }catch(e){}
20418         }
20419         this.owner.fireEvent('activate', this);
20420     },
20421
20422     // private
20423     adjustFont: function(btn){
20424         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20425         //if(Roo.isSafari){ // safari
20426         //    adjust *= 2;
20427        // }
20428         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20429         if(Roo.isSafari){ // safari
20430             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20431             v =  (v < 10) ? 10 : v;
20432             v =  (v > 48) ? 48 : v;
20433             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20434             
20435         }
20436         
20437         
20438         v = Math.max(1, v+adjust);
20439         
20440         this.execCmd('FontSize', v  );
20441     },
20442
20443     onEditorEvent : function(e)
20444     {
20445         this.owner.fireEvent('editorevent', this, e);
20446       //  this.updateToolbar();
20447         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20448     },
20449
20450     insertTag : function(tg)
20451     {
20452         // could be a bit smarter... -> wrap the current selected tRoo..
20453         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20454             
20455             range = this.createRange(this.getSelection());
20456             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20457             wrappingNode.appendChild(range.extractContents());
20458             range.insertNode(wrappingNode);
20459
20460             return;
20461             
20462             
20463             
20464         }
20465         this.execCmd("formatblock",   tg);
20466         
20467     },
20468     
20469     insertText : function(txt)
20470     {
20471         
20472         
20473         var range = this.createRange();
20474         range.deleteContents();
20475                //alert(Sender.getAttribute('label'));
20476                
20477         range.insertNode(this.doc.createTextNode(txt));
20478     } ,
20479     
20480      
20481
20482     /**
20483      * Executes a Midas editor command on the editor document and performs necessary focus and
20484      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20485      * @param {String} cmd The Midas command
20486      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20487      */
20488     relayCmd : function(cmd, value){
20489         this.win.focus();
20490         this.execCmd(cmd, value);
20491         this.owner.fireEvent('editorevent', this);
20492         //this.updateToolbar();
20493         this.owner.deferFocus();
20494     },
20495
20496     /**
20497      * Executes a Midas editor command directly on the editor document.
20498      * For visual commands, you should use {@link #relayCmd} instead.
20499      * <b>This should only be called after the editor is initialized.</b>
20500      * @param {String} cmd The Midas command
20501      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20502      */
20503     execCmd : function(cmd, value){
20504         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20505         this.syncValue();
20506     },
20507  
20508  
20509    
20510     /**
20511      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20512      * to insert tRoo.
20513      * @param {String} text | dom node.. 
20514      */
20515     insertAtCursor : function(text)
20516     {
20517         
20518         
20519         
20520         if(!this.activated){
20521             return;
20522         }
20523         /*
20524         if(Roo.isIE){
20525             this.win.focus();
20526             var r = this.doc.selection.createRange();
20527             if(r){
20528                 r.collapse(true);
20529                 r.pasteHTML(text);
20530                 this.syncValue();
20531                 this.deferFocus();
20532             
20533             }
20534             return;
20535         }
20536         */
20537         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20538             this.win.focus();
20539             
20540             
20541             // from jquery ui (MIT licenced)
20542             var range, node;
20543             var win = this.win;
20544             
20545             if (win.getSelection && win.getSelection().getRangeAt) {
20546                 range = win.getSelection().getRangeAt(0);
20547                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20548                 range.insertNode(node);
20549             } else if (win.document.selection && win.document.selection.createRange) {
20550                 // no firefox support
20551                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20552                 win.document.selection.createRange().pasteHTML(txt);
20553             } else {
20554                 // no firefox support
20555                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20556                 this.execCmd('InsertHTML', txt);
20557             } 
20558             
20559             this.syncValue();
20560             
20561             this.deferFocus();
20562         }
20563     },
20564  // private
20565     mozKeyPress : function(e){
20566         if(e.ctrlKey){
20567             var c = e.getCharCode(), cmd;
20568           
20569             if(c > 0){
20570                 c = String.fromCharCode(c).toLowerCase();
20571                 switch(c){
20572                     case 'b':
20573                         cmd = 'bold';
20574                         break;
20575                     case 'i':
20576                         cmd = 'italic';
20577                         break;
20578                     
20579                     case 'u':
20580                         cmd = 'underline';
20581                         break;
20582                     
20583                     case 'v':
20584                         this.cleanUpPaste.defer(100, this);
20585                         return;
20586                         
20587                 }
20588                 if(cmd){
20589                     this.win.focus();
20590                     this.execCmd(cmd);
20591                     this.deferFocus();
20592                     e.preventDefault();
20593                 }
20594                 
20595             }
20596         }
20597     },
20598
20599     // private
20600     fixKeys : function(){ // load time branching for fastest keydown performance
20601         if(Roo.isIE){
20602             return function(e){
20603                 var k = e.getKey(), r;
20604                 if(k == e.TAB){
20605                     e.stopEvent();
20606                     r = this.doc.selection.createRange();
20607                     if(r){
20608                         r.collapse(true);
20609                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20610                         this.deferFocus();
20611                     }
20612                     return;
20613                 }
20614                 
20615                 if(k == e.ENTER){
20616                     r = this.doc.selection.createRange();
20617                     if(r){
20618                         var target = r.parentElement();
20619                         if(!target || target.tagName.toLowerCase() != 'li'){
20620                             e.stopEvent();
20621                             r.pasteHTML('<br />');
20622                             r.collapse(false);
20623                             r.select();
20624                         }
20625                     }
20626                 }
20627                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20628                     this.cleanUpPaste.defer(100, this);
20629                     return;
20630                 }
20631                 
20632                 
20633             };
20634         }else if(Roo.isOpera){
20635             return function(e){
20636                 var k = e.getKey();
20637                 if(k == e.TAB){
20638                     e.stopEvent();
20639                     this.win.focus();
20640                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20641                     this.deferFocus();
20642                 }
20643                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20644                     this.cleanUpPaste.defer(100, this);
20645                     return;
20646                 }
20647                 
20648             };
20649         }else if(Roo.isSafari){
20650             return function(e){
20651                 var k = e.getKey();
20652                 
20653                 if(k == e.TAB){
20654                     e.stopEvent();
20655                     this.execCmd('InsertText','\t');
20656                     this.deferFocus();
20657                     return;
20658                 }
20659                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20660                     this.cleanUpPaste.defer(100, this);
20661                     return;
20662                 }
20663                 
20664              };
20665         }
20666     }(),
20667     
20668     getAllAncestors: function()
20669     {
20670         var p = this.getSelectedNode();
20671         var a = [];
20672         if (!p) {
20673             a.push(p); // push blank onto stack..
20674             p = this.getParentElement();
20675         }
20676         
20677         
20678         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20679             a.push(p);
20680             p = p.parentNode;
20681         }
20682         a.push(this.doc.body);
20683         return a;
20684     },
20685     lastSel : false,
20686     lastSelNode : false,
20687     
20688     
20689     getSelection : function() 
20690     {
20691         this.assignDocWin();
20692         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20693     },
20694     
20695     getSelectedNode: function() 
20696     {
20697         // this may only work on Gecko!!!
20698         
20699         // should we cache this!!!!
20700         
20701         
20702         
20703          
20704         var range = this.createRange(this.getSelection()).cloneRange();
20705         
20706         if (Roo.isIE) {
20707             var parent = range.parentElement();
20708             while (true) {
20709                 var testRange = range.duplicate();
20710                 testRange.moveToElementText(parent);
20711                 if (testRange.inRange(range)) {
20712                     break;
20713                 }
20714                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20715                     break;
20716                 }
20717                 parent = parent.parentElement;
20718             }
20719             return parent;
20720         }
20721         
20722         // is ancestor a text element.
20723         var ac =  range.commonAncestorContainer;
20724         if (ac.nodeType == 3) {
20725             ac = ac.parentNode;
20726         }
20727         
20728         var ar = ac.childNodes;
20729          
20730         var nodes = [];
20731         var other_nodes = [];
20732         var has_other_nodes = false;
20733         for (var i=0;i<ar.length;i++) {
20734             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20735                 continue;
20736             }
20737             // fullly contained node.
20738             
20739             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20740                 nodes.push(ar[i]);
20741                 continue;
20742             }
20743             
20744             // probably selected..
20745             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20746                 other_nodes.push(ar[i]);
20747                 continue;
20748             }
20749             // outer..
20750             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20751                 continue;
20752             }
20753             
20754             
20755             has_other_nodes = true;
20756         }
20757         if (!nodes.length && other_nodes.length) {
20758             nodes= other_nodes;
20759         }
20760         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20761             return false;
20762         }
20763         
20764         return nodes[0];
20765     },
20766     createRange: function(sel)
20767     {
20768         // this has strange effects when using with 
20769         // top toolbar - not sure if it's a great idea.
20770         //this.editor.contentWindow.focus();
20771         if (typeof sel != "undefined") {
20772             try {
20773                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20774             } catch(e) {
20775                 return this.doc.createRange();
20776             }
20777         } else {
20778             return this.doc.createRange();
20779         }
20780     },
20781     getParentElement: function()
20782     {
20783         
20784         this.assignDocWin();
20785         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20786         
20787         var range = this.createRange(sel);
20788          
20789         try {
20790             var p = range.commonAncestorContainer;
20791             while (p.nodeType == 3) { // text node
20792                 p = p.parentNode;
20793             }
20794             return p;
20795         } catch (e) {
20796             return null;
20797         }
20798     
20799     },
20800     /***
20801      *
20802      * Range intersection.. the hard stuff...
20803      *  '-1' = before
20804      *  '0' = hits..
20805      *  '1' = after.
20806      *         [ -- selected range --- ]
20807      *   [fail]                        [fail]
20808      *
20809      *    basically..
20810      *      if end is before start or  hits it. fail.
20811      *      if start is after end or hits it fail.
20812      *
20813      *   if either hits (but other is outside. - then it's not 
20814      *   
20815      *    
20816      **/
20817     
20818     
20819     // @see http://www.thismuchiknow.co.uk/?p=64.
20820     rangeIntersectsNode : function(range, node)
20821     {
20822         var nodeRange = node.ownerDocument.createRange();
20823         try {
20824             nodeRange.selectNode(node);
20825         } catch (e) {
20826             nodeRange.selectNodeContents(node);
20827         }
20828     
20829         var rangeStartRange = range.cloneRange();
20830         rangeStartRange.collapse(true);
20831     
20832         var rangeEndRange = range.cloneRange();
20833         rangeEndRange.collapse(false);
20834     
20835         var nodeStartRange = nodeRange.cloneRange();
20836         nodeStartRange.collapse(true);
20837     
20838         var nodeEndRange = nodeRange.cloneRange();
20839         nodeEndRange.collapse(false);
20840     
20841         return rangeStartRange.compareBoundaryPoints(
20842                  Range.START_TO_START, nodeEndRange) == -1 &&
20843                rangeEndRange.compareBoundaryPoints(
20844                  Range.START_TO_START, nodeStartRange) == 1;
20845         
20846          
20847     },
20848     rangeCompareNode : function(range, node)
20849     {
20850         var nodeRange = node.ownerDocument.createRange();
20851         try {
20852             nodeRange.selectNode(node);
20853         } catch (e) {
20854             nodeRange.selectNodeContents(node);
20855         }
20856         
20857         
20858         range.collapse(true);
20859     
20860         nodeRange.collapse(true);
20861      
20862         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20863         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20864          
20865         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20866         
20867         var nodeIsBefore   =  ss == 1;
20868         var nodeIsAfter    = ee == -1;
20869         
20870         if (nodeIsBefore && nodeIsAfter) {
20871             return 0; // outer
20872         }
20873         if (!nodeIsBefore && nodeIsAfter) {
20874             return 1; //right trailed.
20875         }
20876         
20877         if (nodeIsBefore && !nodeIsAfter) {
20878             return 2;  // left trailed.
20879         }
20880         // fully contined.
20881         return 3;
20882     },
20883
20884     // private? - in a new class?
20885     cleanUpPaste :  function()
20886     {
20887         // cleans up the whole document..
20888         Roo.log('cleanuppaste');
20889         
20890         this.cleanUpChildren(this.doc.body);
20891         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20892         if (clean != this.doc.body.innerHTML) {
20893             this.doc.body.innerHTML = clean;
20894         }
20895         
20896     },
20897     
20898     cleanWordChars : function(input) {// change the chars to hex code
20899         var he = Roo.HtmlEditorCore;
20900         
20901         var output = input;
20902         Roo.each(he.swapCodes, function(sw) { 
20903             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20904             
20905             output = output.replace(swapper, sw[1]);
20906         });
20907         
20908         return output;
20909     },
20910     
20911     
20912     cleanUpChildren : function (n)
20913     {
20914         if (!n.childNodes.length) {
20915             return;
20916         }
20917         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20918            this.cleanUpChild(n.childNodes[i]);
20919         }
20920     },
20921     
20922     
20923         
20924     
20925     cleanUpChild : function (node)
20926     {
20927         var ed = this;
20928         //console.log(node);
20929         if (node.nodeName == "#text") {
20930             // clean up silly Windows -- stuff?
20931             return; 
20932         }
20933         if (node.nodeName == "#comment") {
20934             node.parentNode.removeChild(node);
20935             // clean up silly Windows -- stuff?
20936             return; 
20937         }
20938         var lcname = node.tagName.toLowerCase();
20939         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20940         // whitelist of tags..
20941         
20942         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20943             // remove node.
20944             node.parentNode.removeChild(node);
20945             return;
20946             
20947         }
20948         
20949         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20950         
20951         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20952         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20953         
20954         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20955         //    remove_keep_children = true;
20956         //}
20957         
20958         if (remove_keep_children) {
20959             this.cleanUpChildren(node);
20960             // inserts everything just before this node...
20961             while (node.childNodes.length) {
20962                 var cn = node.childNodes[0];
20963                 node.removeChild(cn);
20964                 node.parentNode.insertBefore(cn, node);
20965             }
20966             node.parentNode.removeChild(node);
20967             return;
20968         }
20969         
20970         if (!node.attributes || !node.attributes.length) {
20971             this.cleanUpChildren(node);
20972             return;
20973         }
20974         
20975         function cleanAttr(n,v)
20976         {
20977             
20978             if (v.match(/^\./) || v.match(/^\//)) {
20979                 return;
20980             }
20981             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20982                 return;
20983             }
20984             if (v.match(/^#/)) {
20985                 return;
20986             }
20987 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20988             node.removeAttribute(n);
20989             
20990         }
20991         
20992         var cwhite = this.cwhite;
20993         var cblack = this.cblack;
20994             
20995         function cleanStyle(n,v)
20996         {
20997             if (v.match(/expression/)) { //XSS?? should we even bother..
20998                 node.removeAttribute(n);
20999                 return;
21000             }
21001             
21002             var parts = v.split(/;/);
21003             var clean = [];
21004             
21005             Roo.each(parts, function(p) {
21006                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21007                 if (!p.length) {
21008                     return true;
21009                 }
21010                 var l = p.split(':').shift().replace(/\s+/g,'');
21011                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21012                 
21013                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21014 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21015                     //node.removeAttribute(n);
21016                     return true;
21017                 }
21018                 //Roo.log()
21019                 // only allow 'c whitelisted system attributes'
21020                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21021 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21022                     //node.removeAttribute(n);
21023                     return true;
21024                 }
21025                 
21026                 
21027                  
21028                 
21029                 clean.push(p);
21030                 return true;
21031             });
21032             if (clean.length) { 
21033                 node.setAttribute(n, clean.join(';'));
21034             } else {
21035                 node.removeAttribute(n);
21036             }
21037             
21038         }
21039         
21040         
21041         for (var i = node.attributes.length-1; i > -1 ; i--) {
21042             var a = node.attributes[i];
21043             //console.log(a);
21044             
21045             if (a.name.toLowerCase().substr(0,2)=='on')  {
21046                 node.removeAttribute(a.name);
21047                 continue;
21048             }
21049             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21050                 node.removeAttribute(a.name);
21051                 continue;
21052             }
21053             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21054                 cleanAttr(a.name,a.value); // fixme..
21055                 continue;
21056             }
21057             if (a.name == 'style') {
21058                 cleanStyle(a.name,a.value);
21059                 continue;
21060             }
21061             /// clean up MS crap..
21062             // tecnically this should be a list of valid class'es..
21063             
21064             
21065             if (a.name == 'class') {
21066                 if (a.value.match(/^Mso/)) {
21067                     node.className = '';
21068                 }
21069                 
21070                 if (a.value.match(/body/)) {
21071                     node.className = '';
21072                 }
21073                 continue;
21074             }
21075             
21076             // style cleanup!?
21077             // class cleanup?
21078             
21079         }
21080         
21081         
21082         this.cleanUpChildren(node);
21083         
21084         
21085     },
21086     
21087     /**
21088      * Clean up MS wordisms...
21089      */
21090     cleanWord : function(node)
21091     {
21092         
21093         
21094         if (!node) {
21095             this.cleanWord(this.doc.body);
21096             return;
21097         }
21098         if (node.nodeName == "#text") {
21099             // clean up silly Windows -- stuff?
21100             return; 
21101         }
21102         if (node.nodeName == "#comment") {
21103             node.parentNode.removeChild(node);
21104             // clean up silly Windows -- stuff?
21105             return; 
21106         }
21107         
21108         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21109             node.parentNode.removeChild(node);
21110             return;
21111         }
21112         
21113         // remove - but keep children..
21114         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21115             while (node.childNodes.length) {
21116                 var cn = node.childNodes[0];
21117                 node.removeChild(cn);
21118                 node.parentNode.insertBefore(cn, node);
21119             }
21120             node.parentNode.removeChild(node);
21121             this.iterateChildren(node, this.cleanWord);
21122             return;
21123         }
21124         // clean styles
21125         if (node.className.length) {
21126             
21127             var cn = node.className.split(/\W+/);
21128             var cna = [];
21129             Roo.each(cn, function(cls) {
21130                 if (cls.match(/Mso[a-zA-Z]+/)) {
21131                     return;
21132                 }
21133                 cna.push(cls);
21134             });
21135             node.className = cna.length ? cna.join(' ') : '';
21136             if (!cna.length) {
21137                 node.removeAttribute("class");
21138             }
21139         }
21140         
21141         if (node.hasAttribute("lang")) {
21142             node.removeAttribute("lang");
21143         }
21144         
21145         if (node.hasAttribute("style")) {
21146             
21147             var styles = node.getAttribute("style").split(";");
21148             var nstyle = [];
21149             Roo.each(styles, function(s) {
21150                 if (!s.match(/:/)) {
21151                     return;
21152                 }
21153                 var kv = s.split(":");
21154                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21155                     return;
21156                 }
21157                 // what ever is left... we allow.
21158                 nstyle.push(s);
21159             });
21160             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21161             if (!nstyle.length) {
21162                 node.removeAttribute('style');
21163             }
21164         }
21165         this.iterateChildren(node, this.cleanWord);
21166         
21167         
21168         
21169     },
21170     /**
21171      * iterateChildren of a Node, calling fn each time, using this as the scole..
21172      * @param {DomNode} node node to iterate children of.
21173      * @param {Function} fn method of this class to call on each item.
21174      */
21175     iterateChildren : function(node, fn)
21176     {
21177         if (!node.childNodes.length) {
21178                 return;
21179         }
21180         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21181            fn.call(this, node.childNodes[i])
21182         }
21183     },
21184     
21185     
21186     /**
21187      * cleanTableWidths.
21188      *
21189      * Quite often pasting from word etc.. results in tables with column and widths.
21190      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21191      *
21192      */
21193     cleanTableWidths : function(node)
21194     {
21195          
21196          
21197         if (!node) {
21198             this.cleanTableWidths(this.doc.body);
21199             return;
21200         }
21201         
21202         // ignore list...
21203         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21204             return; 
21205         }
21206         Roo.log(node.tagName);
21207         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21208             this.iterateChildren(node, this.cleanTableWidths);
21209             return;
21210         }
21211         if (node.hasAttribute('width')) {
21212             node.removeAttribute('width');
21213         }
21214         
21215          
21216         if (node.hasAttribute("style")) {
21217             // pretty basic...
21218             
21219             var styles = node.getAttribute("style").split(";");
21220             var nstyle = [];
21221             Roo.each(styles, function(s) {
21222                 if (!s.match(/:/)) {
21223                     return;
21224                 }
21225                 var kv = s.split(":");
21226                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21227                     return;
21228                 }
21229                 // what ever is left... we allow.
21230                 nstyle.push(s);
21231             });
21232             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21233             if (!nstyle.length) {
21234                 node.removeAttribute('style');
21235             }
21236         }
21237         
21238         this.iterateChildren(node, this.cleanTableWidths);
21239         
21240         
21241     },
21242     
21243     
21244     
21245     
21246     domToHTML : function(currentElement, depth, nopadtext) {
21247         
21248         depth = depth || 0;
21249         nopadtext = nopadtext || false;
21250     
21251         if (!currentElement) {
21252             return this.domToHTML(this.doc.body);
21253         }
21254         
21255         //Roo.log(currentElement);
21256         var j;
21257         var allText = false;
21258         var nodeName = currentElement.nodeName;
21259         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21260         
21261         if  (nodeName == '#text') {
21262             
21263             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21264         }
21265         
21266         
21267         var ret = '';
21268         if (nodeName != 'BODY') {
21269              
21270             var i = 0;
21271             // Prints the node tagName, such as <A>, <IMG>, etc
21272             if (tagName) {
21273                 var attr = [];
21274                 for(i = 0; i < currentElement.attributes.length;i++) {
21275                     // quoting?
21276                     var aname = currentElement.attributes.item(i).name;
21277                     if (!currentElement.attributes.item(i).value.length) {
21278                         continue;
21279                     }
21280                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21281                 }
21282                 
21283                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21284             } 
21285             else {
21286                 
21287                 // eack
21288             }
21289         } else {
21290             tagName = false;
21291         }
21292         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21293             return ret;
21294         }
21295         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21296             nopadtext = true;
21297         }
21298         
21299         
21300         // Traverse the tree
21301         i = 0;
21302         var currentElementChild = currentElement.childNodes.item(i);
21303         var allText = true;
21304         var innerHTML  = '';
21305         lastnode = '';
21306         while (currentElementChild) {
21307             // Formatting code (indent the tree so it looks nice on the screen)
21308             var nopad = nopadtext;
21309             if (lastnode == 'SPAN') {
21310                 nopad  = true;
21311             }
21312             // text
21313             if  (currentElementChild.nodeName == '#text') {
21314                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21315                 toadd = nopadtext ? toadd : toadd.trim();
21316                 if (!nopad && toadd.length > 80) {
21317                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21318                 }
21319                 innerHTML  += toadd;
21320                 
21321                 i++;
21322                 currentElementChild = currentElement.childNodes.item(i);
21323                 lastNode = '';
21324                 continue;
21325             }
21326             allText = false;
21327             
21328             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21329                 
21330             // Recursively traverse the tree structure of the child node
21331             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21332             lastnode = currentElementChild.nodeName;
21333             i++;
21334             currentElementChild=currentElement.childNodes.item(i);
21335         }
21336         
21337         ret += innerHTML;
21338         
21339         if (!allText) {
21340                 // The remaining code is mostly for formatting the tree
21341             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21342         }
21343         
21344         
21345         if (tagName) {
21346             ret+= "</"+tagName+">";
21347         }
21348         return ret;
21349         
21350     },
21351         
21352     applyBlacklists : function()
21353     {
21354         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21355         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21356         
21357         this.white = [];
21358         this.black = [];
21359         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21360             if (b.indexOf(tag) > -1) {
21361                 return;
21362             }
21363             this.white.push(tag);
21364             
21365         }, this);
21366         
21367         Roo.each(w, function(tag) {
21368             if (b.indexOf(tag) > -1) {
21369                 return;
21370             }
21371             if (this.white.indexOf(tag) > -1) {
21372                 return;
21373             }
21374             this.white.push(tag);
21375             
21376         }, this);
21377         
21378         
21379         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21380             if (w.indexOf(tag) > -1) {
21381                 return;
21382             }
21383             this.black.push(tag);
21384             
21385         }, this);
21386         
21387         Roo.each(b, function(tag) {
21388             if (w.indexOf(tag) > -1) {
21389                 return;
21390             }
21391             if (this.black.indexOf(tag) > -1) {
21392                 return;
21393             }
21394             this.black.push(tag);
21395             
21396         }, this);
21397         
21398         
21399         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21400         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21401         
21402         this.cwhite = [];
21403         this.cblack = [];
21404         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21405             if (b.indexOf(tag) > -1) {
21406                 return;
21407             }
21408             this.cwhite.push(tag);
21409             
21410         }, this);
21411         
21412         Roo.each(w, function(tag) {
21413             if (b.indexOf(tag) > -1) {
21414                 return;
21415             }
21416             if (this.cwhite.indexOf(tag) > -1) {
21417                 return;
21418             }
21419             this.cwhite.push(tag);
21420             
21421         }, this);
21422         
21423         
21424         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21425             if (w.indexOf(tag) > -1) {
21426                 return;
21427             }
21428             this.cblack.push(tag);
21429             
21430         }, this);
21431         
21432         Roo.each(b, function(tag) {
21433             if (w.indexOf(tag) > -1) {
21434                 return;
21435             }
21436             if (this.cblack.indexOf(tag) > -1) {
21437                 return;
21438             }
21439             this.cblack.push(tag);
21440             
21441         }, this);
21442     },
21443     
21444     setStylesheets : function(stylesheets)
21445     {
21446         if(typeof(stylesheets) == 'string'){
21447             Roo.get(this.iframe.contentDocument.head).createChild({
21448                 tag : 'link',
21449                 rel : 'stylesheet',
21450                 type : 'text/css',
21451                 href : stylesheets
21452             });
21453             
21454             return;
21455         }
21456         var _this = this;
21457      
21458         Roo.each(stylesheets, function(s) {
21459             if(!s.length){
21460                 return;
21461             }
21462             
21463             Roo.get(_this.iframe.contentDocument.head).createChild({
21464                 tag : 'link',
21465                 rel : 'stylesheet',
21466                 type : 'text/css',
21467                 href : s
21468             });
21469         });
21470
21471         
21472     },
21473     
21474     removeStylesheets : function()
21475     {
21476         var _this = this;
21477         
21478         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21479             s.remove();
21480         });
21481     }
21482     
21483     // hide stuff that is not compatible
21484     /**
21485      * @event blur
21486      * @hide
21487      */
21488     /**
21489      * @event change
21490      * @hide
21491      */
21492     /**
21493      * @event focus
21494      * @hide
21495      */
21496     /**
21497      * @event specialkey
21498      * @hide
21499      */
21500     /**
21501      * @cfg {String} fieldClass @hide
21502      */
21503     /**
21504      * @cfg {String} focusClass @hide
21505      */
21506     /**
21507      * @cfg {String} autoCreate @hide
21508      */
21509     /**
21510      * @cfg {String} inputType @hide
21511      */
21512     /**
21513      * @cfg {String} invalidClass @hide
21514      */
21515     /**
21516      * @cfg {String} invalidText @hide
21517      */
21518     /**
21519      * @cfg {String} msgFx @hide
21520      */
21521     /**
21522      * @cfg {String} validateOnBlur @hide
21523      */
21524 });
21525
21526 Roo.HtmlEditorCore.white = [
21527         'area', 'br', 'img', 'input', 'hr', 'wbr',
21528         
21529        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21530        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21531        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21532        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21533        'table',   'ul',         'xmp', 
21534        
21535        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21536       'thead',   'tr', 
21537      
21538       'dir', 'menu', 'ol', 'ul', 'dl',
21539        
21540       'embed',  'object'
21541 ];
21542
21543
21544 Roo.HtmlEditorCore.black = [
21545     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21546         'applet', // 
21547         'base',   'basefont', 'bgsound', 'blink',  'body', 
21548         'frame',  'frameset', 'head',    'html',   'ilayer', 
21549         'iframe', 'layer',  'link',     'meta',    'object',   
21550         'script', 'style' ,'title',  'xml' // clean later..
21551 ];
21552 Roo.HtmlEditorCore.clean = [
21553     'script', 'style', 'title', 'xml'
21554 ];
21555 Roo.HtmlEditorCore.remove = [
21556     'font'
21557 ];
21558 // attributes..
21559
21560 Roo.HtmlEditorCore.ablack = [
21561     'on'
21562 ];
21563     
21564 Roo.HtmlEditorCore.aclean = [ 
21565     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21566 ];
21567
21568 // protocols..
21569 Roo.HtmlEditorCore.pwhite= [
21570         'http',  'https',  'mailto'
21571 ];
21572
21573 // white listed style attributes.
21574 Roo.HtmlEditorCore.cwhite= [
21575       //  'text-align', /// default is to allow most things..
21576       
21577          
21578 //        'font-size'//??
21579 ];
21580
21581 // black listed style attributes.
21582 Roo.HtmlEditorCore.cblack= [
21583       //  'font-size' -- this can be set by the project 
21584 ];
21585
21586
21587 Roo.HtmlEditorCore.swapCodes   =[ 
21588     [    8211, "--" ], 
21589     [    8212, "--" ], 
21590     [    8216,  "'" ],  
21591     [    8217, "'" ],  
21592     [    8220, '"' ],  
21593     [    8221, '"' ],  
21594     [    8226, "*" ],  
21595     [    8230, "..." ]
21596 ]; 
21597
21598     /*
21599  * - LGPL
21600  *
21601  * HtmlEditor
21602  * 
21603  */
21604
21605 /**
21606  * @class Roo.bootstrap.HtmlEditor
21607  * @extends Roo.bootstrap.TextArea
21608  * Bootstrap HtmlEditor class
21609
21610  * @constructor
21611  * Create a new HtmlEditor
21612  * @param {Object} config The config object
21613  */
21614
21615 Roo.bootstrap.HtmlEditor = function(config){
21616     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21617     if (!this.toolbars) {
21618         this.toolbars = [];
21619     }
21620     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21621     this.addEvents({
21622             /**
21623              * @event initialize
21624              * Fires when the editor is fully initialized (including the iframe)
21625              * @param {HtmlEditor} this
21626              */
21627             initialize: true,
21628             /**
21629              * @event activate
21630              * Fires when the editor is first receives the focus. Any insertion must wait
21631              * until after this event.
21632              * @param {HtmlEditor} this
21633              */
21634             activate: true,
21635              /**
21636              * @event beforesync
21637              * Fires before the textarea is updated with content from the editor iframe. Return false
21638              * to cancel the sync.
21639              * @param {HtmlEditor} this
21640              * @param {String} html
21641              */
21642             beforesync: true,
21643              /**
21644              * @event beforepush
21645              * Fires before the iframe editor is updated with content from the textarea. Return false
21646              * to cancel the push.
21647              * @param {HtmlEditor} this
21648              * @param {String} html
21649              */
21650             beforepush: true,
21651              /**
21652              * @event sync
21653              * Fires when the textarea is updated with content from the editor iframe.
21654              * @param {HtmlEditor} this
21655              * @param {String} html
21656              */
21657             sync: true,
21658              /**
21659              * @event push
21660              * Fires when the iframe editor is updated with content from the textarea.
21661              * @param {HtmlEditor} this
21662              * @param {String} html
21663              */
21664             push: true,
21665              /**
21666              * @event editmodechange
21667              * Fires when the editor switches edit modes
21668              * @param {HtmlEditor} this
21669              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21670              */
21671             editmodechange: true,
21672             /**
21673              * @event editorevent
21674              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21675              * @param {HtmlEditor} this
21676              */
21677             editorevent: true,
21678             /**
21679              * @event firstfocus
21680              * Fires when on first focus - needed by toolbars..
21681              * @param {HtmlEditor} this
21682              */
21683             firstfocus: true,
21684             /**
21685              * @event autosave
21686              * Auto save the htmlEditor value as a file into Events
21687              * @param {HtmlEditor} this
21688              */
21689             autosave: true,
21690             /**
21691              * @event savedpreview
21692              * preview the saved version of htmlEditor
21693              * @param {HtmlEditor} this
21694              */
21695             savedpreview: true
21696         });
21697 };
21698
21699
21700 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21701     
21702     
21703       /**
21704      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21705      */
21706     toolbars : false,
21707    
21708      /**
21709      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21710      *                        Roo.resizable.
21711      */
21712     resizable : false,
21713      /**
21714      * @cfg {Number} height (in pixels)
21715      */   
21716     height: 300,
21717    /**
21718      * @cfg {Number} width (in pixels)
21719      */   
21720     width: false,
21721     
21722     /**
21723      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21724      * 
21725      */
21726     stylesheets: false,
21727     
21728     // id of frame..
21729     frameId: false,
21730     
21731     // private properties
21732     validationEvent : false,
21733     deferHeight: true,
21734     initialized : false,
21735     activated : false,
21736     
21737     onFocus : Roo.emptyFn,
21738     iframePad:3,
21739     hideMode:'offsets',
21740     
21741     
21742     tbContainer : false,
21743     
21744     toolbarContainer :function() {
21745         return this.wrap.select('.x-html-editor-tb',true).first();
21746     },
21747
21748     /**
21749      * Protected method that will not generally be called directly. It
21750      * is called when the editor creates its toolbar. Override this method if you need to
21751      * add custom toolbar buttons.
21752      * @param {HtmlEditor} editor
21753      */
21754     createToolbar : function(){
21755         
21756         Roo.log("create toolbars");
21757         
21758         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21759         this.toolbars[0].render(this.toolbarContainer());
21760         
21761         return;
21762         
21763 //        if (!editor.toolbars || !editor.toolbars.length) {
21764 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21765 //        }
21766 //        
21767 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21768 //            editor.toolbars[i] = Roo.factory(
21769 //                    typeof(editor.toolbars[i]) == 'string' ?
21770 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21771 //                Roo.bootstrap.HtmlEditor);
21772 //            editor.toolbars[i].init(editor);
21773 //        }
21774     },
21775
21776      
21777     // private
21778     onRender : function(ct, position)
21779     {
21780        // Roo.log("Call onRender: " + this.xtype);
21781         var _t = this;
21782         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21783       
21784         this.wrap = this.inputEl().wrap({
21785             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21786         });
21787         
21788         this.editorcore.onRender(ct, position);
21789          
21790         if (this.resizable) {
21791             this.resizeEl = new Roo.Resizable(this.wrap, {
21792                 pinned : true,
21793                 wrap: true,
21794                 dynamic : true,
21795                 minHeight : this.height,
21796                 height: this.height,
21797                 handles : this.resizable,
21798                 width: this.width,
21799                 listeners : {
21800                     resize : function(r, w, h) {
21801                         _t.onResize(w,h); // -something
21802                     }
21803                 }
21804             });
21805             
21806         }
21807         this.createToolbar(this);
21808        
21809         
21810         if(!this.width && this.resizable){
21811             this.setSize(this.wrap.getSize());
21812         }
21813         if (this.resizeEl) {
21814             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21815             // should trigger onReize..
21816         }
21817         
21818     },
21819
21820     // private
21821     onResize : function(w, h)
21822     {
21823         Roo.log('resize: ' +w + ',' + h );
21824         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21825         var ew = false;
21826         var eh = false;
21827         
21828         if(this.inputEl() ){
21829             if(typeof w == 'number'){
21830                 var aw = w - this.wrap.getFrameWidth('lr');
21831                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21832                 ew = aw;
21833             }
21834             if(typeof h == 'number'){
21835                  var tbh = -11;  // fixme it needs to tool bar size!
21836                 for (var i =0; i < this.toolbars.length;i++) {
21837                     // fixme - ask toolbars for heights?
21838                     tbh += this.toolbars[i].el.getHeight();
21839                     //if (this.toolbars[i].footer) {
21840                     //    tbh += this.toolbars[i].footer.el.getHeight();
21841                     //}
21842                 }
21843               
21844                 
21845                 
21846                 
21847                 
21848                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21849                 ah -= 5; // knock a few pixes off for look..
21850                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21851                 var eh = ah;
21852             }
21853         }
21854         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21855         this.editorcore.onResize(ew,eh);
21856         
21857     },
21858
21859     /**
21860      * Toggles the editor between standard and source edit mode.
21861      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21862      */
21863     toggleSourceEdit : function(sourceEditMode)
21864     {
21865         this.editorcore.toggleSourceEdit(sourceEditMode);
21866         
21867         if(this.editorcore.sourceEditMode){
21868             Roo.log('editor - showing textarea');
21869             
21870 //            Roo.log('in');
21871 //            Roo.log(this.syncValue());
21872             this.syncValue();
21873             this.inputEl().removeClass(['hide', 'x-hidden']);
21874             this.inputEl().dom.removeAttribute('tabIndex');
21875             this.inputEl().focus();
21876         }else{
21877             Roo.log('editor - hiding textarea');
21878 //            Roo.log('out')
21879 //            Roo.log(this.pushValue()); 
21880             this.pushValue();
21881             
21882             this.inputEl().addClass(['hide', 'x-hidden']);
21883             this.inputEl().dom.setAttribute('tabIndex', -1);
21884             //this.deferFocus();
21885         }
21886          
21887         if(this.resizable){
21888             this.setSize(this.wrap.getSize());
21889         }
21890         
21891         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21892     },
21893  
21894     // private (for BoxComponent)
21895     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21896
21897     // private (for BoxComponent)
21898     getResizeEl : function(){
21899         return this.wrap;
21900     },
21901
21902     // private (for BoxComponent)
21903     getPositionEl : function(){
21904         return this.wrap;
21905     },
21906
21907     // private
21908     initEvents : function(){
21909         this.originalValue = this.getValue();
21910     },
21911
21912 //    /**
21913 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21914 //     * @method
21915 //     */
21916 //    markInvalid : Roo.emptyFn,
21917 //    /**
21918 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21919 //     * @method
21920 //     */
21921 //    clearInvalid : Roo.emptyFn,
21922
21923     setValue : function(v){
21924         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21925         this.editorcore.pushValue();
21926     },
21927
21928      
21929     // private
21930     deferFocus : function(){
21931         this.focus.defer(10, this);
21932     },
21933
21934     // doc'ed in Field
21935     focus : function(){
21936         this.editorcore.focus();
21937         
21938     },
21939       
21940
21941     // private
21942     onDestroy : function(){
21943         
21944         
21945         
21946         if(this.rendered){
21947             
21948             for (var i =0; i < this.toolbars.length;i++) {
21949                 // fixme - ask toolbars for heights?
21950                 this.toolbars[i].onDestroy();
21951             }
21952             
21953             this.wrap.dom.innerHTML = '';
21954             this.wrap.remove();
21955         }
21956     },
21957
21958     // private
21959     onFirstFocus : function(){
21960         //Roo.log("onFirstFocus");
21961         this.editorcore.onFirstFocus();
21962          for (var i =0; i < this.toolbars.length;i++) {
21963             this.toolbars[i].onFirstFocus();
21964         }
21965         
21966     },
21967     
21968     // private
21969     syncValue : function()
21970     {   
21971         this.editorcore.syncValue();
21972     },
21973     
21974     pushValue : function()
21975     {   
21976         this.editorcore.pushValue();
21977     }
21978      
21979     
21980     // hide stuff that is not compatible
21981     /**
21982      * @event blur
21983      * @hide
21984      */
21985     /**
21986      * @event change
21987      * @hide
21988      */
21989     /**
21990      * @event focus
21991      * @hide
21992      */
21993     /**
21994      * @event specialkey
21995      * @hide
21996      */
21997     /**
21998      * @cfg {String} fieldClass @hide
21999      */
22000     /**
22001      * @cfg {String} focusClass @hide
22002      */
22003     /**
22004      * @cfg {String} autoCreate @hide
22005      */
22006     /**
22007      * @cfg {String} inputType @hide
22008      */
22009     /**
22010      * @cfg {String} invalidClass @hide
22011      */
22012     /**
22013      * @cfg {String} invalidText @hide
22014      */
22015     /**
22016      * @cfg {String} msgFx @hide
22017      */
22018     /**
22019      * @cfg {String} validateOnBlur @hide
22020      */
22021 });
22022  
22023     
22024    
22025    
22026    
22027       
22028 Roo.namespace('Roo.bootstrap.htmleditor');
22029 /**
22030  * @class Roo.bootstrap.HtmlEditorToolbar1
22031  * Basic Toolbar
22032  * 
22033  * Usage:
22034  *
22035  new Roo.bootstrap.HtmlEditor({
22036     ....
22037     toolbars : [
22038         new Roo.bootstrap.HtmlEditorToolbar1({
22039             disable : { fonts: 1 , format: 1, ..., ... , ...],
22040             btns : [ .... ]
22041         })
22042     }
22043      
22044  * 
22045  * @cfg {Object} disable List of elements to disable..
22046  * @cfg {Array} btns List of additional buttons.
22047  * 
22048  * 
22049  * NEEDS Extra CSS? 
22050  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22051  */
22052  
22053 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22054 {
22055     
22056     Roo.apply(this, config);
22057     
22058     // default disabled, based on 'good practice'..
22059     this.disable = this.disable || {};
22060     Roo.applyIf(this.disable, {
22061         fontSize : true,
22062         colors : true,
22063         specialElements : true
22064     });
22065     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22066     
22067     this.editor = config.editor;
22068     this.editorcore = config.editor.editorcore;
22069     
22070     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22071     
22072     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22073     // dont call parent... till later.
22074 }
22075 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22076      
22077     bar : true,
22078     
22079     editor : false,
22080     editorcore : false,
22081     
22082     
22083     formats : [
22084         "p" ,  
22085         "h1","h2","h3","h4","h5","h6", 
22086         "pre", "code", 
22087         "abbr", "acronym", "address", "cite", "samp", "var",
22088         'div','span'
22089     ],
22090     
22091     onRender : function(ct, position)
22092     {
22093        // Roo.log("Call onRender: " + this.xtype);
22094         
22095        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22096        Roo.log(this.el);
22097        this.el.dom.style.marginBottom = '0';
22098        var _this = this;
22099        var editorcore = this.editorcore;
22100        var editor= this.editor;
22101        
22102        var children = [];
22103        var btn = function(id,cmd , toggle, handler){
22104        
22105             var  event = toggle ? 'toggle' : 'click';
22106        
22107             var a = {
22108                 size : 'sm',
22109                 xtype: 'Button',
22110                 xns: Roo.bootstrap,
22111                 glyphicon : id,
22112                 cmd : id || cmd,
22113                 enableToggle:toggle !== false,
22114                 //html : 'submit'
22115                 pressed : toggle ? false : null,
22116                 listeners : {}
22117             };
22118             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22119                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22120             };
22121             children.push(a);
22122             return a;
22123        }
22124         
22125         var style = {
22126                 xtype: 'Button',
22127                 size : 'sm',
22128                 xns: Roo.bootstrap,
22129                 glyphicon : 'font',
22130                 //html : 'submit'
22131                 menu : {
22132                     xtype: 'Menu',
22133                     xns: Roo.bootstrap,
22134                     items:  []
22135                 }
22136         };
22137         Roo.each(this.formats, function(f) {
22138             style.menu.items.push({
22139                 xtype :'MenuItem',
22140                 xns: Roo.bootstrap,
22141                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22142                 tagname : f,
22143                 listeners : {
22144                     click : function()
22145                     {
22146                         editorcore.insertTag(this.tagname);
22147                         editor.focus();
22148                     }
22149                 }
22150                 
22151             });
22152         });
22153          children.push(style);   
22154             
22155             
22156         btn('bold',false,true);
22157         btn('italic',false,true);
22158         btn('align-left', 'justifyleft',true);
22159         btn('align-center', 'justifycenter',true);
22160         btn('align-right' , 'justifyright',true);
22161         btn('link', false, false, function(btn) {
22162             //Roo.log("create link?");
22163             var url = prompt(this.createLinkText, this.defaultLinkValue);
22164             if(url && url != 'http:/'+'/'){
22165                 this.editorcore.relayCmd('createlink', url);
22166             }
22167         }),
22168         btn('list','insertunorderedlist',true);
22169         btn('pencil', false,true, function(btn){
22170                 Roo.log(this);
22171                 
22172                 this.toggleSourceEdit(btn.pressed);
22173         });
22174         /*
22175         var cog = {
22176                 xtype: 'Button',
22177                 size : 'sm',
22178                 xns: Roo.bootstrap,
22179                 glyphicon : 'cog',
22180                 //html : 'submit'
22181                 menu : {
22182                     xtype: 'Menu',
22183                     xns: Roo.bootstrap,
22184                     items:  []
22185                 }
22186         };
22187         
22188         cog.menu.items.push({
22189             xtype :'MenuItem',
22190             xns: Roo.bootstrap,
22191             html : Clean styles,
22192             tagname : f,
22193             listeners : {
22194                 click : function()
22195                 {
22196                     editorcore.insertTag(this.tagname);
22197                     editor.focus();
22198                 }
22199             }
22200             
22201         });
22202        */
22203         
22204          
22205        this.xtype = 'NavSimplebar';
22206         
22207         for(var i=0;i< children.length;i++) {
22208             
22209             this.buttons.add(this.addxtypeChild(children[i]));
22210             
22211         }
22212         
22213         editor.on('editorevent', this.updateToolbar, this);
22214     },
22215     onBtnClick : function(id)
22216     {
22217        this.editorcore.relayCmd(id);
22218        this.editorcore.focus();
22219     },
22220     
22221     /**
22222      * Protected method that will not generally be called directly. It triggers
22223      * a toolbar update by reading the markup state of the current selection in the editor.
22224      */
22225     updateToolbar: function(){
22226
22227         if(!this.editorcore.activated){
22228             this.editor.onFirstFocus(); // is this neeed?
22229             return;
22230         }
22231
22232         var btns = this.buttons; 
22233         var doc = this.editorcore.doc;
22234         btns.get('bold').setActive(doc.queryCommandState('bold'));
22235         btns.get('italic').setActive(doc.queryCommandState('italic'));
22236         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22237         
22238         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22239         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22240         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22241         
22242         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22243         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22244          /*
22245         
22246         var ans = this.editorcore.getAllAncestors();
22247         if (this.formatCombo) {
22248             
22249             
22250             var store = this.formatCombo.store;
22251             this.formatCombo.setValue("");
22252             for (var i =0; i < ans.length;i++) {
22253                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22254                     // select it..
22255                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22256                     break;
22257                 }
22258             }
22259         }
22260         
22261         
22262         
22263         // hides menus... - so this cant be on a menu...
22264         Roo.bootstrap.MenuMgr.hideAll();
22265         */
22266         Roo.bootstrap.MenuMgr.hideAll();
22267         //this.editorsyncValue();
22268     },
22269     onFirstFocus: function() {
22270         this.buttons.each(function(item){
22271            item.enable();
22272         });
22273     },
22274     toggleSourceEdit : function(sourceEditMode){
22275         
22276           
22277         if(sourceEditMode){
22278             Roo.log("disabling buttons");
22279            this.buttons.each( function(item){
22280                 if(item.cmd != 'pencil'){
22281                     item.disable();
22282                 }
22283             });
22284           
22285         }else{
22286             Roo.log("enabling buttons");
22287             if(this.editorcore.initialized){
22288                 this.buttons.each( function(item){
22289                     item.enable();
22290                 });
22291             }
22292             
22293         }
22294         Roo.log("calling toggole on editor");
22295         // tell the editor that it's been pressed..
22296         this.editor.toggleSourceEdit(sourceEditMode);
22297        
22298     }
22299 });
22300
22301
22302
22303
22304
22305 /**
22306  * @class Roo.bootstrap.Table.AbstractSelectionModel
22307  * @extends Roo.util.Observable
22308  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22309  * implemented by descendant classes.  This class should not be directly instantiated.
22310  * @constructor
22311  */
22312 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22313     this.locked = false;
22314     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22315 };
22316
22317
22318 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22319     /** @ignore Called by the grid automatically. Do not call directly. */
22320     init : function(grid){
22321         this.grid = grid;
22322         this.initEvents();
22323     },
22324
22325     /**
22326      * Locks the selections.
22327      */
22328     lock : function(){
22329         this.locked = true;
22330     },
22331
22332     /**
22333      * Unlocks the selections.
22334      */
22335     unlock : function(){
22336         this.locked = false;
22337     },
22338
22339     /**
22340      * Returns true if the selections are locked.
22341      * @return {Boolean}
22342      */
22343     isLocked : function(){
22344         return this.locked;
22345     }
22346 });
22347 /**
22348  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22349  * @class Roo.bootstrap.Table.RowSelectionModel
22350  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22351  * It supports multiple selections and keyboard selection/navigation. 
22352  * @constructor
22353  * @param {Object} config
22354  */
22355
22356 Roo.bootstrap.Table.RowSelectionModel = function(config){
22357     Roo.apply(this, config);
22358     this.selections = new Roo.util.MixedCollection(false, function(o){
22359         return o.id;
22360     });
22361
22362     this.last = false;
22363     this.lastActive = false;
22364
22365     this.addEvents({
22366         /**
22367              * @event selectionchange
22368              * Fires when the selection changes
22369              * @param {SelectionModel} this
22370              */
22371             "selectionchange" : true,
22372         /**
22373              * @event afterselectionchange
22374              * Fires after the selection changes (eg. by key press or clicking)
22375              * @param {SelectionModel} this
22376              */
22377             "afterselectionchange" : true,
22378         /**
22379              * @event beforerowselect
22380              * Fires when a row is selected being selected, return false to cancel.
22381              * @param {SelectionModel} this
22382              * @param {Number} rowIndex The selected index
22383              * @param {Boolean} keepExisting False if other selections will be cleared
22384              */
22385             "beforerowselect" : true,
22386         /**
22387              * @event rowselect
22388              * Fires when a row is selected.
22389              * @param {SelectionModel} this
22390              * @param {Number} rowIndex The selected index
22391              * @param {Roo.data.Record} r The record
22392              */
22393             "rowselect" : true,
22394         /**
22395              * @event rowdeselect
22396              * Fires when a row is deselected.
22397              * @param {SelectionModel} this
22398              * @param {Number} rowIndex The selected index
22399              */
22400         "rowdeselect" : true
22401     });
22402     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22403     this.locked = false;
22404 };
22405
22406 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22407     /**
22408      * @cfg {Boolean} singleSelect
22409      * True to allow selection of only one row at a time (defaults to false)
22410      */
22411     singleSelect : false,
22412
22413     // private
22414     initEvents : function(){
22415
22416         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22417             this.grid.on("mousedown", this.handleMouseDown, this);
22418         }else{ // allow click to work like normal
22419             this.grid.on("rowclick", this.handleDragableRowClick, this);
22420         }
22421
22422         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22423             "up" : function(e){
22424                 if(!e.shiftKey){
22425                     this.selectPrevious(e.shiftKey);
22426                 }else if(this.last !== false && this.lastActive !== false){
22427                     var last = this.last;
22428                     this.selectRange(this.last,  this.lastActive-1);
22429                     this.grid.getView().focusRow(this.lastActive);
22430                     if(last !== false){
22431                         this.last = last;
22432                     }
22433                 }else{
22434                     this.selectFirstRow();
22435                 }
22436                 this.fireEvent("afterselectionchange", this);
22437             },
22438             "down" : function(e){
22439                 if(!e.shiftKey){
22440                     this.selectNext(e.shiftKey);
22441                 }else if(this.last !== false && this.lastActive !== false){
22442                     var last = this.last;
22443                     this.selectRange(this.last,  this.lastActive+1);
22444                     this.grid.getView().focusRow(this.lastActive);
22445                     if(last !== false){
22446                         this.last = last;
22447                     }
22448                 }else{
22449                     this.selectFirstRow();
22450                 }
22451                 this.fireEvent("afterselectionchange", this);
22452             },
22453             scope: this
22454         });
22455
22456         var view = this.grid.view;
22457         view.on("refresh", this.onRefresh, this);
22458         view.on("rowupdated", this.onRowUpdated, this);
22459         view.on("rowremoved", this.onRemove, this);
22460     },
22461
22462     // private
22463     onRefresh : function(){
22464         var ds = this.grid.dataSource, i, v = this.grid.view;
22465         var s = this.selections;
22466         s.each(function(r){
22467             if((i = ds.indexOfId(r.id)) != -1){
22468                 v.onRowSelect(i);
22469             }else{
22470                 s.remove(r);
22471             }
22472         });
22473     },
22474
22475     // private
22476     onRemove : function(v, index, r){
22477         this.selections.remove(r);
22478     },
22479
22480     // private
22481     onRowUpdated : function(v, index, r){
22482         if(this.isSelected(r)){
22483             v.onRowSelect(index);
22484         }
22485     },
22486
22487     /**
22488      * Select records.
22489      * @param {Array} records The records to select
22490      * @param {Boolean} keepExisting (optional) True to keep existing selections
22491      */
22492     selectRecords : function(records, keepExisting){
22493         if(!keepExisting){
22494             this.clearSelections();
22495         }
22496         var ds = this.grid.dataSource;
22497         for(var i = 0, len = records.length; i < len; i++){
22498             this.selectRow(ds.indexOf(records[i]), true);
22499         }
22500     },
22501
22502     /**
22503      * Gets the number of selected rows.
22504      * @return {Number}
22505      */
22506     getCount : function(){
22507         return this.selections.length;
22508     },
22509
22510     /**
22511      * Selects the first row in the grid.
22512      */
22513     selectFirstRow : function(){
22514         this.selectRow(0);
22515     },
22516
22517     /**
22518      * Select the last row.
22519      * @param {Boolean} keepExisting (optional) True to keep existing selections
22520      */
22521     selectLastRow : function(keepExisting){
22522         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22523     },
22524
22525     /**
22526      * Selects the row immediately following the last selected row.
22527      * @param {Boolean} keepExisting (optional) True to keep existing selections
22528      */
22529     selectNext : function(keepExisting){
22530         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22531             this.selectRow(this.last+1, keepExisting);
22532             this.grid.getView().focusRow(this.last);
22533         }
22534     },
22535
22536     /**
22537      * Selects the row that precedes the last selected row.
22538      * @param {Boolean} keepExisting (optional) True to keep existing selections
22539      */
22540     selectPrevious : function(keepExisting){
22541         if(this.last){
22542             this.selectRow(this.last-1, keepExisting);
22543             this.grid.getView().focusRow(this.last);
22544         }
22545     },
22546
22547     /**
22548      * Returns the selected records
22549      * @return {Array} Array of selected records
22550      */
22551     getSelections : function(){
22552         return [].concat(this.selections.items);
22553     },
22554
22555     /**
22556      * Returns the first selected record.
22557      * @return {Record}
22558      */
22559     getSelected : function(){
22560         return this.selections.itemAt(0);
22561     },
22562
22563
22564     /**
22565      * Clears all selections.
22566      */
22567     clearSelections : function(fast){
22568         if(this.locked) {
22569             return;
22570         }
22571         if(fast !== true){
22572             var ds = this.grid.dataSource;
22573             var s = this.selections;
22574             s.each(function(r){
22575                 this.deselectRow(ds.indexOfId(r.id));
22576             }, this);
22577             s.clear();
22578         }else{
22579             this.selections.clear();
22580         }
22581         this.last = false;
22582     },
22583
22584
22585     /**
22586      * Selects all rows.
22587      */
22588     selectAll : function(){
22589         if(this.locked) {
22590             return;
22591         }
22592         this.selections.clear();
22593         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22594             this.selectRow(i, true);
22595         }
22596     },
22597
22598     /**
22599      * Returns True if there is a selection.
22600      * @return {Boolean}
22601      */
22602     hasSelection : function(){
22603         return this.selections.length > 0;
22604     },
22605
22606     /**
22607      * Returns True if the specified row is selected.
22608      * @param {Number/Record} record The record or index of the record to check
22609      * @return {Boolean}
22610      */
22611     isSelected : function(index){
22612         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22613         return (r && this.selections.key(r.id) ? true : false);
22614     },
22615
22616     /**
22617      * Returns True if the specified record id is selected.
22618      * @param {String} id The id of record to check
22619      * @return {Boolean}
22620      */
22621     isIdSelected : function(id){
22622         return (this.selections.key(id) ? true : false);
22623     },
22624
22625     // private
22626     handleMouseDown : function(e, t){
22627         var view = this.grid.getView(), rowIndex;
22628         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22629             return;
22630         };
22631         if(e.shiftKey && this.last !== false){
22632             var last = this.last;
22633             this.selectRange(last, rowIndex, e.ctrlKey);
22634             this.last = last; // reset the last
22635             view.focusRow(rowIndex);
22636         }else{
22637             var isSelected = this.isSelected(rowIndex);
22638             if(e.button !== 0 && isSelected){
22639                 view.focusRow(rowIndex);
22640             }else if(e.ctrlKey && isSelected){
22641                 this.deselectRow(rowIndex);
22642             }else if(!isSelected){
22643                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22644                 view.focusRow(rowIndex);
22645             }
22646         }
22647         this.fireEvent("afterselectionchange", this);
22648     },
22649     // private
22650     handleDragableRowClick :  function(grid, rowIndex, e) 
22651     {
22652         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22653             this.selectRow(rowIndex, false);
22654             grid.view.focusRow(rowIndex);
22655              this.fireEvent("afterselectionchange", this);
22656         }
22657     },
22658     
22659     /**
22660      * Selects multiple rows.
22661      * @param {Array} rows Array of the indexes of the row to select
22662      * @param {Boolean} keepExisting (optional) True to keep existing selections
22663      */
22664     selectRows : function(rows, keepExisting){
22665         if(!keepExisting){
22666             this.clearSelections();
22667         }
22668         for(var i = 0, len = rows.length; i < len; i++){
22669             this.selectRow(rows[i], true);
22670         }
22671     },
22672
22673     /**
22674      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22675      * @param {Number} startRow The index of the first row in the range
22676      * @param {Number} endRow The index of the last row in the range
22677      * @param {Boolean} keepExisting (optional) True to retain existing selections
22678      */
22679     selectRange : function(startRow, endRow, keepExisting){
22680         if(this.locked) {
22681             return;
22682         }
22683         if(!keepExisting){
22684             this.clearSelections();
22685         }
22686         if(startRow <= endRow){
22687             for(var i = startRow; i <= endRow; i++){
22688                 this.selectRow(i, true);
22689             }
22690         }else{
22691             for(var i = startRow; i >= endRow; i--){
22692                 this.selectRow(i, true);
22693             }
22694         }
22695     },
22696
22697     /**
22698      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22699      * @param {Number} startRow The index of the first row in the range
22700      * @param {Number} endRow The index of the last row in the range
22701      */
22702     deselectRange : function(startRow, endRow, preventViewNotify){
22703         if(this.locked) {
22704             return;
22705         }
22706         for(var i = startRow; i <= endRow; i++){
22707             this.deselectRow(i, preventViewNotify);
22708         }
22709     },
22710
22711     /**
22712      * Selects a row.
22713      * @param {Number} row The index of the row to select
22714      * @param {Boolean} keepExisting (optional) True to keep existing selections
22715      */
22716     selectRow : function(index, keepExisting, preventViewNotify){
22717         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22718             return;
22719         }
22720         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22721             if(!keepExisting || this.singleSelect){
22722                 this.clearSelections();
22723             }
22724             var r = this.grid.dataSource.getAt(index);
22725             this.selections.add(r);
22726             this.last = this.lastActive = index;
22727             if(!preventViewNotify){
22728                 this.grid.getView().onRowSelect(index);
22729             }
22730             this.fireEvent("rowselect", this, index, r);
22731             this.fireEvent("selectionchange", this);
22732         }
22733     },
22734
22735     /**
22736      * Deselects a row.
22737      * @param {Number} row The index of the row to deselect
22738      */
22739     deselectRow : function(index, preventViewNotify){
22740         if(this.locked) {
22741             return;
22742         }
22743         if(this.last == index){
22744             this.last = false;
22745         }
22746         if(this.lastActive == index){
22747             this.lastActive = false;
22748         }
22749         var r = this.grid.dataSource.getAt(index);
22750         this.selections.remove(r);
22751         if(!preventViewNotify){
22752             this.grid.getView().onRowDeselect(index);
22753         }
22754         this.fireEvent("rowdeselect", this, index);
22755         this.fireEvent("selectionchange", this);
22756     },
22757
22758     // private
22759     restoreLast : function(){
22760         if(this._last){
22761             this.last = this._last;
22762         }
22763     },
22764
22765     // private
22766     acceptsNav : function(row, col, cm){
22767         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22768     },
22769
22770     // private
22771     onEditorKey : function(field, e){
22772         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22773         if(k == e.TAB){
22774             e.stopEvent();
22775             ed.completeEdit();
22776             if(e.shiftKey){
22777                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22778             }else{
22779                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22780             }
22781         }else if(k == e.ENTER && !e.ctrlKey){
22782             e.stopEvent();
22783             ed.completeEdit();
22784             if(e.shiftKey){
22785                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22786             }else{
22787                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22788             }
22789         }else if(k == e.ESC){
22790             ed.cancelEdit();
22791         }
22792         if(newCell){
22793             g.startEditing(newCell[0], newCell[1]);
22794         }
22795     }
22796 });/*
22797  * Based on:
22798  * Ext JS Library 1.1.1
22799  * Copyright(c) 2006-2007, Ext JS, LLC.
22800  *
22801  * Originally Released Under LGPL - original licence link has changed is not relivant.
22802  *
22803  * Fork - LGPL
22804  * <script type="text/javascript">
22805  */
22806  
22807 /**
22808  * @class Roo.bootstrap.PagingToolbar
22809  * @extends Roo.bootstrap.NavSimplebar
22810  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22811  * @constructor
22812  * Create a new PagingToolbar
22813  * @param {Object} config The config object
22814  * @param {Roo.data.Store} store
22815  */
22816 Roo.bootstrap.PagingToolbar = function(config)
22817 {
22818     // old args format still supported... - xtype is prefered..
22819         // created from xtype...
22820     
22821     this.ds = config.dataSource;
22822     
22823     if (config.store && !this.ds) {
22824         this.store= Roo.factory(config.store, Roo.data);
22825         this.ds = this.store;
22826         this.ds.xmodule = this.xmodule || false;
22827     }
22828     
22829     this.toolbarItems = [];
22830     if (config.items) {
22831         this.toolbarItems = config.items;
22832     }
22833     
22834     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22835     
22836     this.cursor = 0;
22837     
22838     if (this.ds) { 
22839         this.bind(this.ds);
22840     }
22841     
22842     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22843     
22844 };
22845
22846 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22847     /**
22848      * @cfg {Roo.data.Store} dataSource
22849      * The underlying data store providing the paged data
22850      */
22851     /**
22852      * @cfg {String/HTMLElement/Element} container
22853      * container The id or element that will contain the toolbar
22854      */
22855     /**
22856      * @cfg {Boolean} displayInfo
22857      * True to display the displayMsg (defaults to false)
22858      */
22859     /**
22860      * @cfg {Number} pageSize
22861      * The number of records to display per page (defaults to 20)
22862      */
22863     pageSize: 20,
22864     /**
22865      * @cfg {String} displayMsg
22866      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22867      */
22868     displayMsg : 'Displaying {0} - {1} of {2}',
22869     /**
22870      * @cfg {String} emptyMsg
22871      * The message to display when no records are found (defaults to "No data to display")
22872      */
22873     emptyMsg : 'No data to display',
22874     /**
22875      * Customizable piece of the default paging text (defaults to "Page")
22876      * @type String
22877      */
22878     beforePageText : "Page",
22879     /**
22880      * Customizable piece of the default paging text (defaults to "of %0")
22881      * @type String
22882      */
22883     afterPageText : "of {0}",
22884     /**
22885      * Customizable piece of the default paging text (defaults to "First Page")
22886      * @type String
22887      */
22888     firstText : "First Page",
22889     /**
22890      * Customizable piece of the default paging text (defaults to "Previous Page")
22891      * @type String
22892      */
22893     prevText : "Previous Page",
22894     /**
22895      * Customizable piece of the default paging text (defaults to "Next Page")
22896      * @type String
22897      */
22898     nextText : "Next Page",
22899     /**
22900      * Customizable piece of the default paging text (defaults to "Last Page")
22901      * @type String
22902      */
22903     lastText : "Last Page",
22904     /**
22905      * Customizable piece of the default paging text (defaults to "Refresh")
22906      * @type String
22907      */
22908     refreshText : "Refresh",
22909
22910     buttons : false,
22911     // private
22912     onRender : function(ct, position) 
22913     {
22914         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22915         this.navgroup.parentId = this.id;
22916         this.navgroup.onRender(this.el, null);
22917         // add the buttons to the navgroup
22918         
22919         if(this.displayInfo){
22920             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22921             this.displayEl = this.el.select('.x-paging-info', true).first();
22922 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22923 //            this.displayEl = navel.el.select('span',true).first();
22924         }
22925         
22926         var _this = this;
22927         
22928         if(this.buttons){
22929             Roo.each(_this.buttons, function(e){ // this might need to use render????
22930                Roo.factory(e).onRender(_this.el, null);
22931             });
22932         }
22933             
22934         Roo.each(_this.toolbarItems, function(e) {
22935             _this.navgroup.addItem(e);
22936         });
22937         
22938         
22939         this.first = this.navgroup.addItem({
22940             tooltip: this.firstText,
22941             cls: "prev",
22942             icon : 'fa fa-backward',
22943             disabled: true,
22944             preventDefault: true,
22945             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22946         });
22947         
22948         this.prev =  this.navgroup.addItem({
22949             tooltip: this.prevText,
22950             cls: "prev",
22951             icon : 'fa fa-step-backward',
22952             disabled: true,
22953             preventDefault: true,
22954             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22955         });
22956     //this.addSeparator();
22957         
22958         
22959         var field = this.navgroup.addItem( {
22960             tagtype : 'span',
22961             cls : 'x-paging-position',
22962             
22963             html : this.beforePageText  +
22964                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22965                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22966          } ); //?? escaped?
22967         
22968         this.field = field.el.select('input', true).first();
22969         this.field.on("keydown", this.onPagingKeydown, this);
22970         this.field.on("focus", function(){this.dom.select();});
22971     
22972     
22973         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22974         //this.field.setHeight(18);
22975         //this.addSeparator();
22976         this.next = this.navgroup.addItem({
22977             tooltip: this.nextText,
22978             cls: "next",
22979             html : ' <i class="fa fa-step-forward">',
22980             disabled: true,
22981             preventDefault: true,
22982             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22983         });
22984         this.last = this.navgroup.addItem({
22985             tooltip: this.lastText,
22986             icon : 'fa fa-forward',
22987             cls: "next",
22988             disabled: true,
22989             preventDefault: true,
22990             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22991         });
22992     //this.addSeparator();
22993         this.loading = this.navgroup.addItem({
22994             tooltip: this.refreshText,
22995             icon: 'fa fa-refresh',
22996             preventDefault: true,
22997             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22998         });
22999         
23000     },
23001
23002     // private
23003     updateInfo : function(){
23004         if(this.displayEl){
23005             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23006             var msg = count == 0 ?
23007                 this.emptyMsg :
23008                 String.format(
23009                     this.displayMsg,
23010                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23011                 );
23012             this.displayEl.update(msg);
23013         }
23014     },
23015
23016     // private
23017     onLoad : function(ds, r, o){
23018        this.cursor = o.params ? o.params.start : 0;
23019        var d = this.getPageData(),
23020             ap = d.activePage,
23021             ps = d.pages;
23022         
23023        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23024        this.field.dom.value = ap;
23025        this.first.setDisabled(ap == 1);
23026        this.prev.setDisabled(ap == 1);
23027        this.next.setDisabled(ap == ps);
23028        this.last.setDisabled(ap == ps);
23029        this.loading.enable();
23030        this.updateInfo();
23031     },
23032
23033     // private
23034     getPageData : function(){
23035         var total = this.ds.getTotalCount();
23036         return {
23037             total : total,
23038             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23039             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23040         };
23041     },
23042
23043     // private
23044     onLoadError : function(){
23045         this.loading.enable();
23046     },
23047
23048     // private
23049     onPagingKeydown : function(e){
23050         var k = e.getKey();
23051         var d = this.getPageData();
23052         if(k == e.RETURN){
23053             var v = this.field.dom.value, pageNum;
23054             if(!v || isNaN(pageNum = parseInt(v, 10))){
23055                 this.field.dom.value = d.activePage;
23056                 return;
23057             }
23058             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23059             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23060             e.stopEvent();
23061         }
23062         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))
23063         {
23064           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23065           this.field.dom.value = pageNum;
23066           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23067           e.stopEvent();
23068         }
23069         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23070         {
23071           var v = this.field.dom.value, pageNum; 
23072           var increment = (e.shiftKey) ? 10 : 1;
23073           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23074                 increment *= -1;
23075           }
23076           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23077             this.field.dom.value = d.activePage;
23078             return;
23079           }
23080           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23081           {
23082             this.field.dom.value = parseInt(v, 10) + increment;
23083             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23084             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23085           }
23086           e.stopEvent();
23087         }
23088     },
23089
23090     // private
23091     beforeLoad : function(){
23092         if(this.loading){
23093             this.loading.disable();
23094         }
23095     },
23096
23097     // private
23098     onClick : function(which){
23099         
23100         var ds = this.ds;
23101         if (!ds) {
23102             return;
23103         }
23104         
23105         switch(which){
23106             case "first":
23107                 ds.load({params:{start: 0, limit: this.pageSize}});
23108             break;
23109             case "prev":
23110                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23111             break;
23112             case "next":
23113                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23114             break;
23115             case "last":
23116                 var total = ds.getTotalCount();
23117                 var extra = total % this.pageSize;
23118                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23119                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23120             break;
23121             case "refresh":
23122                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23123             break;
23124         }
23125     },
23126
23127     /**
23128      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23129      * @param {Roo.data.Store} store The data store to unbind
23130      */
23131     unbind : function(ds){
23132         ds.un("beforeload", this.beforeLoad, this);
23133         ds.un("load", this.onLoad, this);
23134         ds.un("loadexception", this.onLoadError, this);
23135         ds.un("remove", this.updateInfo, this);
23136         ds.un("add", this.updateInfo, this);
23137         this.ds = undefined;
23138     },
23139
23140     /**
23141      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23142      * @param {Roo.data.Store} store The data store to bind
23143      */
23144     bind : function(ds){
23145         ds.on("beforeload", this.beforeLoad, this);
23146         ds.on("load", this.onLoad, this);
23147         ds.on("loadexception", this.onLoadError, this);
23148         ds.on("remove", this.updateInfo, this);
23149         ds.on("add", this.updateInfo, this);
23150         this.ds = ds;
23151     }
23152 });/*
23153  * - LGPL
23154  *
23155  * element
23156  * 
23157  */
23158
23159 /**
23160  * @class Roo.bootstrap.MessageBar
23161  * @extends Roo.bootstrap.Component
23162  * Bootstrap MessageBar class
23163  * @cfg {String} html contents of the MessageBar
23164  * @cfg {String} weight (info | success | warning | danger) default info
23165  * @cfg {String} beforeClass insert the bar before the given class
23166  * @cfg {Boolean} closable (true | false) default false
23167  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23168  * 
23169  * @constructor
23170  * Create a new Element
23171  * @param {Object} config The config object
23172  */
23173
23174 Roo.bootstrap.MessageBar = function(config){
23175     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23176 };
23177
23178 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23179     
23180     html: '',
23181     weight: 'info',
23182     closable: false,
23183     fixed: false,
23184     beforeClass: 'bootstrap-sticky-wrap',
23185     
23186     getAutoCreate : function(){
23187         
23188         var cfg = {
23189             tag: 'div',
23190             cls: 'alert alert-dismissable alert-' + this.weight,
23191             cn: [
23192                 {
23193                     tag: 'span',
23194                     cls: 'message',
23195                     html: this.html || ''
23196                 }
23197             ]
23198         };
23199         
23200         if(this.fixed){
23201             cfg.cls += ' alert-messages-fixed';
23202         }
23203         
23204         if(this.closable){
23205             cfg.cn.push({
23206                 tag: 'button',
23207                 cls: 'close',
23208                 html: 'x'
23209             });
23210         }
23211         
23212         return cfg;
23213     },
23214     
23215     onRender : function(ct, position)
23216     {
23217         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23218         
23219         if(!this.el){
23220             var cfg = Roo.apply({},  this.getAutoCreate());
23221             cfg.id = Roo.id();
23222             
23223             if (this.cls) {
23224                 cfg.cls += ' ' + this.cls;
23225             }
23226             if (this.style) {
23227                 cfg.style = this.style;
23228             }
23229             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23230             
23231             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23232         }
23233         
23234         this.el.select('>button.close').on('click', this.hide, this);
23235         
23236     },
23237     
23238     show : function()
23239     {
23240         if (!this.rendered) {
23241             this.render();
23242         }
23243         
23244         this.el.show();
23245         
23246         this.fireEvent('show', this);
23247         
23248     },
23249     
23250     hide : function()
23251     {
23252         if (!this.rendered) {
23253             this.render();
23254         }
23255         
23256         this.el.hide();
23257         
23258         this.fireEvent('hide', this);
23259     },
23260     
23261     update : function()
23262     {
23263 //        var e = this.el.dom.firstChild;
23264 //        
23265 //        if(this.closable){
23266 //            e = e.nextSibling;
23267 //        }
23268 //        
23269 //        e.data = this.html || '';
23270
23271         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23272     }
23273    
23274 });
23275
23276  
23277
23278      /*
23279  * - LGPL
23280  *
23281  * Graph
23282  * 
23283  */
23284
23285
23286 /**
23287  * @class Roo.bootstrap.Graph
23288  * @extends Roo.bootstrap.Component
23289  * Bootstrap Graph class
23290 > Prameters
23291  -sm {number} sm 4
23292  -md {number} md 5
23293  @cfg {String} graphtype  bar | vbar | pie
23294  @cfg {number} g_x coodinator | centre x (pie)
23295  @cfg {number} g_y coodinator | centre y (pie)
23296  @cfg {number} g_r radius (pie)
23297  @cfg {number} g_height height of the chart (respected by all elements in the set)
23298  @cfg {number} g_width width of the chart (respected by all elements in the set)
23299  @cfg {Object} title The title of the chart
23300     
23301  -{Array}  values
23302  -opts (object) options for the chart 
23303      o {
23304      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23305      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23306      o vgutter (number)
23307      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.
23308      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23309      o to
23310      o stretch (boolean)
23311      o }
23312  -opts (object) options for the pie
23313      o{
23314      o cut
23315      o startAngle (number)
23316      o endAngle (number)
23317      } 
23318  *
23319  * @constructor
23320  * Create a new Input
23321  * @param {Object} config The config object
23322  */
23323
23324 Roo.bootstrap.Graph = function(config){
23325     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23326     
23327     this.addEvents({
23328         // img events
23329         /**
23330          * @event click
23331          * The img click event for the img.
23332          * @param {Roo.EventObject} e
23333          */
23334         "click" : true
23335     });
23336 };
23337
23338 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23339     
23340     sm: 4,
23341     md: 5,
23342     graphtype: 'bar',
23343     g_height: 250,
23344     g_width: 400,
23345     g_x: 50,
23346     g_y: 50,
23347     g_r: 30,
23348     opts:{
23349         //g_colors: this.colors,
23350         g_type: 'soft',
23351         g_gutter: '20%'
23352
23353     },
23354     title : false,
23355
23356     getAutoCreate : function(){
23357         
23358         var cfg = {
23359             tag: 'div',
23360             html : null
23361         };
23362         
23363         
23364         return  cfg;
23365     },
23366
23367     onRender : function(ct,position){
23368         
23369         
23370         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23371         
23372         if (typeof(Raphael) == 'undefined') {
23373             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23374             return;
23375         }
23376         
23377         this.raphael = Raphael(this.el.dom);
23378         
23379                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23380                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23381                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23382                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23383                 /*
23384                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23385                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23386                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23387                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23388                 
23389                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23390                 r.barchart(330, 10, 300, 220, data1);
23391                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23392                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23393                 */
23394                 
23395                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23396                 // r.barchart(30, 30, 560, 250,  xdata, {
23397                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23398                 //     axis : "0 0 1 1",
23399                 //     axisxlabels :  xdata
23400                 //     //yvalues : cols,
23401                    
23402                 // });
23403 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23404 //        
23405 //        this.load(null,xdata,{
23406 //                axis : "0 0 1 1",
23407 //                axisxlabels :  xdata
23408 //                });
23409
23410     },
23411
23412     load : function(graphtype,xdata,opts)
23413     {
23414         this.raphael.clear();
23415         if(!graphtype) {
23416             graphtype = this.graphtype;
23417         }
23418         if(!opts){
23419             opts = this.opts;
23420         }
23421         var r = this.raphael,
23422             fin = function () {
23423                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23424             },
23425             fout = function () {
23426                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23427             },
23428             pfin = function() {
23429                 this.sector.stop();
23430                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23431
23432                 if (this.label) {
23433                     this.label[0].stop();
23434                     this.label[0].attr({ r: 7.5 });
23435                     this.label[1].attr({ "font-weight": 800 });
23436                 }
23437             },
23438             pfout = function() {
23439                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23440
23441                 if (this.label) {
23442                     this.label[0].animate({ r: 5 }, 500, "bounce");
23443                     this.label[1].attr({ "font-weight": 400 });
23444                 }
23445             };
23446
23447         switch(graphtype){
23448             case 'bar':
23449                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23450                 break;
23451             case 'hbar':
23452                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23453                 break;
23454             case 'pie':
23455 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23456 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23457 //            
23458                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23459                 
23460                 break;
23461
23462         }
23463         
23464         if(this.title){
23465             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23466         }
23467         
23468     },
23469     
23470     setTitle: function(o)
23471     {
23472         this.title = o;
23473     },
23474     
23475     initEvents: function() {
23476         
23477         if(!this.href){
23478             this.el.on('click', this.onClick, this);
23479         }
23480     },
23481     
23482     onClick : function(e)
23483     {
23484         Roo.log('img onclick');
23485         this.fireEvent('click', this, e);
23486     }
23487    
23488 });
23489
23490  
23491 /*
23492  * - LGPL
23493  *
23494  * numberBox
23495  * 
23496  */
23497 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23498
23499 /**
23500  * @class Roo.bootstrap.dash.NumberBox
23501  * @extends Roo.bootstrap.Component
23502  * Bootstrap NumberBox class
23503  * @cfg {String} headline Box headline
23504  * @cfg {String} content Box content
23505  * @cfg {String} icon Box icon
23506  * @cfg {String} footer Footer text
23507  * @cfg {String} fhref Footer href
23508  * 
23509  * @constructor
23510  * Create a new NumberBox
23511  * @param {Object} config The config object
23512  */
23513
23514
23515 Roo.bootstrap.dash.NumberBox = function(config){
23516     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23517     
23518 };
23519
23520 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23521     
23522     headline : '',
23523     content : '',
23524     icon : '',
23525     footer : '',
23526     fhref : '',
23527     ficon : '',
23528     
23529     getAutoCreate : function(){
23530         
23531         var cfg = {
23532             tag : 'div',
23533             cls : 'small-box ',
23534             cn : [
23535                 {
23536                     tag : 'div',
23537                     cls : 'inner',
23538                     cn :[
23539                         {
23540                             tag : 'h3',
23541                             cls : 'roo-headline',
23542                             html : this.headline
23543                         },
23544                         {
23545                             tag : 'p',
23546                             cls : 'roo-content',
23547                             html : this.content
23548                         }
23549                     ]
23550                 }
23551             ]
23552         };
23553         
23554         if(this.icon){
23555             cfg.cn.push({
23556                 tag : 'div',
23557                 cls : 'icon',
23558                 cn :[
23559                     {
23560                         tag : 'i',
23561                         cls : 'ion ' + this.icon
23562                     }
23563                 ]
23564             });
23565         }
23566         
23567         if(this.footer){
23568             var footer = {
23569                 tag : 'a',
23570                 cls : 'small-box-footer',
23571                 href : this.fhref || '#',
23572                 html : this.footer
23573             };
23574             
23575             cfg.cn.push(footer);
23576             
23577         }
23578         
23579         return  cfg;
23580     },
23581
23582     onRender : function(ct,position){
23583         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23584
23585
23586        
23587                 
23588     },
23589
23590     setHeadline: function (value)
23591     {
23592         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23593     },
23594     
23595     setFooter: function (value, href)
23596     {
23597         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23598         
23599         if(href){
23600             this.el.select('a.small-box-footer',true).first().attr('href', href);
23601         }
23602         
23603     },
23604
23605     setContent: function (value)
23606     {
23607         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23608     },
23609
23610     initEvents: function() 
23611     {   
23612         
23613     }
23614     
23615 });
23616
23617  
23618 /*
23619  * - LGPL
23620  *
23621  * TabBox
23622  * 
23623  */
23624 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23625
23626 /**
23627  * @class Roo.bootstrap.dash.TabBox
23628  * @extends Roo.bootstrap.Component
23629  * Bootstrap TabBox class
23630  * @cfg {String} title Title of the TabBox
23631  * @cfg {String} icon Icon of the TabBox
23632  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23633  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23634  * 
23635  * @constructor
23636  * Create a new TabBox
23637  * @param {Object} config The config object
23638  */
23639
23640
23641 Roo.bootstrap.dash.TabBox = function(config){
23642     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23643     this.addEvents({
23644         // raw events
23645         /**
23646          * @event addpane
23647          * When a pane is added
23648          * @param {Roo.bootstrap.dash.TabPane} pane
23649          */
23650         "addpane" : true,
23651         /**
23652          * @event activatepane
23653          * When a pane is activated
23654          * @param {Roo.bootstrap.dash.TabPane} pane
23655          */
23656         "activatepane" : true
23657         
23658          
23659     });
23660     
23661     this.panes = [];
23662 };
23663
23664 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23665
23666     title : '',
23667     icon : false,
23668     showtabs : true,
23669     tabScrollable : false,
23670     
23671     getChildContainer : function()
23672     {
23673         return this.el.select('.tab-content', true).first();
23674     },
23675     
23676     getAutoCreate : function(){
23677         
23678         var header = {
23679             tag: 'li',
23680             cls: 'pull-left header',
23681             html: this.title,
23682             cn : []
23683         };
23684         
23685         if(this.icon){
23686             header.cn.push({
23687                 tag: 'i',
23688                 cls: 'fa ' + this.icon
23689             });
23690         }
23691         
23692         var h = {
23693             tag: 'ul',
23694             cls: 'nav nav-tabs pull-right',
23695             cn: [
23696                 header
23697             ]
23698         };
23699         
23700         if(this.tabScrollable){
23701             h = {
23702                 tag: 'div',
23703                 cls: 'tab-header',
23704                 cn: [
23705                     {
23706                         tag: 'ul',
23707                         cls: 'nav nav-tabs pull-right',
23708                         cn: [
23709                             header
23710                         ]
23711                     }
23712                 ]
23713             };
23714         }
23715         
23716         var cfg = {
23717             tag: 'div',
23718             cls: 'nav-tabs-custom',
23719             cn: [
23720                 h,
23721                 {
23722                     tag: 'div',
23723                     cls: 'tab-content no-padding',
23724                     cn: []
23725                 }
23726             ]
23727         };
23728
23729         return  cfg;
23730     },
23731     initEvents : function()
23732     {
23733         //Roo.log('add add pane handler');
23734         this.on('addpane', this.onAddPane, this);
23735     },
23736      /**
23737      * Updates the box title
23738      * @param {String} html to set the title to.
23739      */
23740     setTitle : function(value)
23741     {
23742         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23743     },
23744     onAddPane : function(pane)
23745     {
23746         this.panes.push(pane);
23747         //Roo.log('addpane');
23748         //Roo.log(pane);
23749         // tabs are rendere left to right..
23750         if(!this.showtabs){
23751             return;
23752         }
23753         
23754         var ctr = this.el.select('.nav-tabs', true).first();
23755          
23756          
23757         var existing = ctr.select('.nav-tab',true);
23758         var qty = existing.getCount();;
23759         
23760         
23761         var tab = ctr.createChild({
23762             tag : 'li',
23763             cls : 'nav-tab' + (qty ? '' : ' active'),
23764             cn : [
23765                 {
23766                     tag : 'a',
23767                     href:'#',
23768                     html : pane.title
23769                 }
23770             ]
23771         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23772         pane.tab = tab;
23773         
23774         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23775         if (!qty) {
23776             pane.el.addClass('active');
23777         }
23778         
23779                 
23780     },
23781     onTabClick : function(ev,un,ob,pane)
23782     {
23783         //Roo.log('tab - prev default');
23784         ev.preventDefault();
23785         
23786         
23787         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23788         pane.tab.addClass('active');
23789         //Roo.log(pane.title);
23790         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23791         // technically we should have a deactivate event.. but maybe add later.
23792         // and it should not de-activate the selected tab...
23793         this.fireEvent('activatepane', pane);
23794         pane.el.addClass('active');
23795         pane.fireEvent('activate');
23796         
23797         
23798     },
23799     
23800     getActivePane : function()
23801     {
23802         var r = false;
23803         Roo.each(this.panes, function(p) {
23804             if(p.el.hasClass('active')){
23805                 r = p;
23806                 return false;
23807             }
23808             
23809             return;
23810         });
23811         
23812         return r;
23813     }
23814     
23815     
23816 });
23817
23818  
23819 /*
23820  * - LGPL
23821  *
23822  * Tab pane
23823  * 
23824  */
23825 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23826 /**
23827  * @class Roo.bootstrap.TabPane
23828  * @extends Roo.bootstrap.Component
23829  * Bootstrap TabPane class
23830  * @cfg {Boolean} active (false | true) Default false
23831  * @cfg {String} title title of panel
23832
23833  * 
23834  * @constructor
23835  * Create a new TabPane
23836  * @param {Object} config The config object
23837  */
23838
23839 Roo.bootstrap.dash.TabPane = function(config){
23840     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23841     
23842     this.addEvents({
23843         // raw events
23844         /**
23845          * @event activate
23846          * When a pane is activated
23847          * @param {Roo.bootstrap.dash.TabPane} pane
23848          */
23849         "activate" : true
23850          
23851     });
23852 };
23853
23854 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23855     
23856     active : false,
23857     title : '',
23858     
23859     // the tabBox that this is attached to.
23860     tab : false,
23861      
23862     getAutoCreate : function() 
23863     {
23864         var cfg = {
23865             tag: 'div',
23866             cls: 'tab-pane'
23867         };
23868         
23869         if(this.active){
23870             cfg.cls += ' active';
23871         }
23872         
23873         return cfg;
23874     },
23875     initEvents  : function()
23876     {
23877         //Roo.log('trigger add pane handler');
23878         this.parent().fireEvent('addpane', this)
23879     },
23880     
23881      /**
23882      * Updates the tab title 
23883      * @param {String} html to set the title to.
23884      */
23885     setTitle: function(str)
23886     {
23887         if (!this.tab) {
23888             return;
23889         }
23890         this.title = str;
23891         this.tab.select('a', true).first().dom.innerHTML = str;
23892         
23893     }
23894     
23895     
23896     
23897 });
23898
23899  
23900
23901
23902  /*
23903  * - LGPL
23904  *
23905  * menu
23906  * 
23907  */
23908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23909
23910 /**
23911  * @class Roo.bootstrap.menu.Menu
23912  * @extends Roo.bootstrap.Component
23913  * Bootstrap Menu class - container for Menu
23914  * @cfg {String} html Text of the menu
23915  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23916  * @cfg {String} icon Font awesome icon
23917  * @cfg {String} pos Menu align to (top | bottom) default bottom
23918  * 
23919  * 
23920  * @constructor
23921  * Create a new Menu
23922  * @param {Object} config The config object
23923  */
23924
23925
23926 Roo.bootstrap.menu.Menu = function(config){
23927     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23928     
23929     this.addEvents({
23930         /**
23931          * @event beforeshow
23932          * Fires before this menu is displayed
23933          * @param {Roo.bootstrap.menu.Menu} this
23934          */
23935         beforeshow : true,
23936         /**
23937          * @event beforehide
23938          * Fires before this menu is hidden
23939          * @param {Roo.bootstrap.menu.Menu} this
23940          */
23941         beforehide : true,
23942         /**
23943          * @event show
23944          * Fires after this menu is displayed
23945          * @param {Roo.bootstrap.menu.Menu} this
23946          */
23947         show : true,
23948         /**
23949          * @event hide
23950          * Fires after this menu is hidden
23951          * @param {Roo.bootstrap.menu.Menu} this
23952          */
23953         hide : true,
23954         /**
23955          * @event click
23956          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23957          * @param {Roo.bootstrap.menu.Menu} this
23958          * @param {Roo.EventObject} e
23959          */
23960         click : true
23961     });
23962     
23963 };
23964
23965 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23966     
23967     submenu : false,
23968     html : '',
23969     weight : 'default',
23970     icon : false,
23971     pos : 'bottom',
23972     
23973     
23974     getChildContainer : function() {
23975         if(this.isSubMenu){
23976             return this.el;
23977         }
23978         
23979         return this.el.select('ul.dropdown-menu', true).first();  
23980     },
23981     
23982     getAutoCreate : function()
23983     {
23984         var text = [
23985             {
23986                 tag : 'span',
23987                 cls : 'roo-menu-text',
23988                 html : this.html
23989             }
23990         ];
23991         
23992         if(this.icon){
23993             text.unshift({
23994                 tag : 'i',
23995                 cls : 'fa ' + this.icon
23996             })
23997         }
23998         
23999         
24000         var cfg = {
24001             tag : 'div',
24002             cls : 'btn-group',
24003             cn : [
24004                 {
24005                     tag : 'button',
24006                     cls : 'dropdown-button btn btn-' + this.weight,
24007                     cn : text
24008                 },
24009                 {
24010                     tag : 'button',
24011                     cls : 'dropdown-toggle btn btn-' + this.weight,
24012                     cn : [
24013                         {
24014                             tag : 'span',
24015                             cls : 'caret'
24016                         }
24017                     ]
24018                 },
24019                 {
24020                     tag : 'ul',
24021                     cls : 'dropdown-menu'
24022                 }
24023             ]
24024             
24025         };
24026         
24027         if(this.pos == 'top'){
24028             cfg.cls += ' dropup';
24029         }
24030         
24031         if(this.isSubMenu){
24032             cfg = {
24033                 tag : 'ul',
24034                 cls : 'dropdown-menu'
24035             }
24036         }
24037         
24038         return cfg;
24039     },
24040     
24041     onRender : function(ct, position)
24042     {
24043         this.isSubMenu = ct.hasClass('dropdown-submenu');
24044         
24045         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24046     },
24047     
24048     initEvents : function() 
24049     {
24050         if(this.isSubMenu){
24051             return;
24052         }
24053         
24054         this.hidden = true;
24055         
24056         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24057         this.triggerEl.on('click', this.onTriggerPress, this);
24058         
24059         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24060         this.buttonEl.on('click', this.onClick, this);
24061         
24062     },
24063     
24064     list : function()
24065     {
24066         if(this.isSubMenu){
24067             return this.el;
24068         }
24069         
24070         return this.el.select('ul.dropdown-menu', true).first();
24071     },
24072     
24073     onClick : function(e)
24074     {
24075         this.fireEvent("click", this, e);
24076     },
24077     
24078     onTriggerPress  : function(e)
24079     {   
24080         if (this.isVisible()) {
24081             this.hide();
24082         } else {
24083             this.show();
24084         }
24085     },
24086     
24087     isVisible : function(){
24088         return !this.hidden;
24089     },
24090     
24091     show : function()
24092     {
24093         this.fireEvent("beforeshow", this);
24094         
24095         this.hidden = false;
24096         this.el.addClass('open');
24097         
24098         Roo.get(document).on("mouseup", this.onMouseUp, this);
24099         
24100         this.fireEvent("show", this);
24101         
24102         
24103     },
24104     
24105     hide : function()
24106     {
24107         this.fireEvent("beforehide", this);
24108         
24109         this.hidden = true;
24110         this.el.removeClass('open');
24111         
24112         Roo.get(document).un("mouseup", this.onMouseUp);
24113         
24114         this.fireEvent("hide", this);
24115     },
24116     
24117     onMouseUp : function()
24118     {
24119         this.hide();
24120     }
24121     
24122 });
24123
24124  
24125  /*
24126  * - LGPL
24127  *
24128  * menu item
24129  * 
24130  */
24131 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24132
24133 /**
24134  * @class Roo.bootstrap.menu.Item
24135  * @extends Roo.bootstrap.Component
24136  * Bootstrap MenuItem class
24137  * @cfg {Boolean} submenu (true | false) default false
24138  * @cfg {String} html text of the item
24139  * @cfg {String} href the link
24140  * @cfg {Boolean} disable (true | false) default false
24141  * @cfg {Boolean} preventDefault (true | false) default true
24142  * @cfg {String} icon Font awesome icon
24143  * @cfg {String} pos Submenu align to (left | right) default right 
24144  * 
24145  * 
24146  * @constructor
24147  * Create a new Item
24148  * @param {Object} config The config object
24149  */
24150
24151
24152 Roo.bootstrap.menu.Item = function(config){
24153     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24154     this.addEvents({
24155         /**
24156          * @event mouseover
24157          * Fires when the mouse is hovering over this menu
24158          * @param {Roo.bootstrap.menu.Item} this
24159          * @param {Roo.EventObject} e
24160          */
24161         mouseover : true,
24162         /**
24163          * @event mouseout
24164          * Fires when the mouse exits this menu
24165          * @param {Roo.bootstrap.menu.Item} this
24166          * @param {Roo.EventObject} e
24167          */
24168         mouseout : true,
24169         // raw events
24170         /**
24171          * @event click
24172          * The raw click event for the entire grid.
24173          * @param {Roo.EventObject} e
24174          */
24175         click : true
24176     });
24177 };
24178
24179 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24180     
24181     submenu : false,
24182     href : '',
24183     html : '',
24184     preventDefault: true,
24185     disable : false,
24186     icon : false,
24187     pos : 'right',
24188     
24189     getAutoCreate : function()
24190     {
24191         var text = [
24192             {
24193                 tag : 'span',
24194                 cls : 'roo-menu-item-text',
24195                 html : this.html
24196             }
24197         ];
24198         
24199         if(this.icon){
24200             text.unshift({
24201                 tag : 'i',
24202                 cls : 'fa ' + this.icon
24203             })
24204         }
24205         
24206         var cfg = {
24207             tag : 'li',
24208             cn : [
24209                 {
24210                     tag : 'a',
24211                     href : this.href || '#',
24212                     cn : text
24213                 }
24214             ]
24215         };
24216         
24217         if(this.disable){
24218             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24219         }
24220         
24221         if(this.submenu){
24222             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24223             
24224             if(this.pos == 'left'){
24225                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24226             }
24227         }
24228         
24229         return cfg;
24230     },
24231     
24232     initEvents : function() 
24233     {
24234         this.el.on('mouseover', this.onMouseOver, this);
24235         this.el.on('mouseout', this.onMouseOut, this);
24236         
24237         this.el.select('a', true).first().on('click', this.onClick, this);
24238         
24239     },
24240     
24241     onClick : function(e)
24242     {
24243         if(this.preventDefault){
24244             e.preventDefault();
24245         }
24246         
24247         this.fireEvent("click", this, e);
24248     },
24249     
24250     onMouseOver : function(e)
24251     {
24252         if(this.submenu && this.pos == 'left'){
24253             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24254         }
24255         
24256         this.fireEvent("mouseover", this, e);
24257     },
24258     
24259     onMouseOut : function(e)
24260     {
24261         this.fireEvent("mouseout", this, e);
24262     }
24263 });
24264
24265  
24266
24267  /*
24268  * - LGPL
24269  *
24270  * menu separator
24271  * 
24272  */
24273 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24274
24275 /**
24276  * @class Roo.bootstrap.menu.Separator
24277  * @extends Roo.bootstrap.Component
24278  * Bootstrap Separator class
24279  * 
24280  * @constructor
24281  * Create a new Separator
24282  * @param {Object} config The config object
24283  */
24284
24285
24286 Roo.bootstrap.menu.Separator = function(config){
24287     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24288 };
24289
24290 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24291     
24292     getAutoCreate : function(){
24293         var cfg = {
24294             tag : 'li',
24295             cls: 'divider'
24296         };
24297         
24298         return cfg;
24299     }
24300    
24301 });
24302
24303  
24304
24305  /*
24306  * - LGPL
24307  *
24308  * Tooltip
24309  * 
24310  */
24311
24312 /**
24313  * @class Roo.bootstrap.Tooltip
24314  * Bootstrap Tooltip class
24315  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24316  * to determine which dom element triggers the tooltip.
24317  * 
24318  * It needs to add support for additional attributes like tooltip-position
24319  * 
24320  * @constructor
24321  * Create a new Toolti
24322  * @param {Object} config The config object
24323  */
24324
24325 Roo.bootstrap.Tooltip = function(config){
24326     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24327 };
24328
24329 Roo.apply(Roo.bootstrap.Tooltip, {
24330     /**
24331      * @function init initialize tooltip monitoring.
24332      * @static
24333      */
24334     currentEl : false,
24335     currentTip : false,
24336     currentRegion : false,
24337     
24338     //  init : delay?
24339     
24340     init : function()
24341     {
24342         Roo.get(document).on('mouseover', this.enter ,this);
24343         Roo.get(document).on('mouseout', this.leave, this);
24344          
24345         
24346         this.currentTip = new Roo.bootstrap.Tooltip();
24347     },
24348     
24349     enter : function(ev)
24350     {
24351         var dom = ev.getTarget();
24352         
24353         //Roo.log(['enter',dom]);
24354         var el = Roo.fly(dom);
24355         if (this.currentEl) {
24356             //Roo.log(dom);
24357             //Roo.log(this.currentEl);
24358             //Roo.log(this.currentEl.contains(dom));
24359             if (this.currentEl == el) {
24360                 return;
24361             }
24362             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24363                 return;
24364             }
24365
24366         }
24367         
24368         if (this.currentTip.el) {
24369             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24370         }    
24371         //Roo.log(ev);
24372         
24373         if(!el || el.dom == document){
24374             return;
24375         }
24376         
24377         var bindEl = el;
24378         
24379         // you can not look for children, as if el is the body.. then everythign is the child..
24380         if (!el.attr('tooltip')) { //
24381             if (!el.select("[tooltip]").elements.length) {
24382                 return;
24383             }
24384             // is the mouse over this child...?
24385             bindEl = el.select("[tooltip]").first();
24386             var xy = ev.getXY();
24387             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24388                 //Roo.log("not in region.");
24389                 return;
24390             }
24391             //Roo.log("child element over..");
24392             
24393         }
24394         this.currentEl = bindEl;
24395         this.currentTip.bind(bindEl);
24396         this.currentRegion = Roo.lib.Region.getRegion(dom);
24397         this.currentTip.enter();
24398         
24399     },
24400     leave : function(ev)
24401     {
24402         var dom = ev.getTarget();
24403         //Roo.log(['leave',dom]);
24404         if (!this.currentEl) {
24405             return;
24406         }
24407         
24408         
24409         if (dom != this.currentEl.dom) {
24410             return;
24411         }
24412         var xy = ev.getXY();
24413         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24414             return;
24415         }
24416         // only activate leave if mouse cursor is outside... bounding box..
24417         
24418         
24419         
24420         
24421         if (this.currentTip) {
24422             this.currentTip.leave();
24423         }
24424         //Roo.log('clear currentEl');
24425         this.currentEl = false;
24426         
24427         
24428     },
24429     alignment : {
24430         'left' : ['r-l', [-2,0], 'right'],
24431         'right' : ['l-r', [2,0], 'left'],
24432         'bottom' : ['t-b', [0,2], 'top'],
24433         'top' : [ 'b-t', [0,-2], 'bottom']
24434     }
24435     
24436 });
24437
24438
24439 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24440     
24441     
24442     bindEl : false,
24443     
24444     delay : null, // can be { show : 300 , hide: 500}
24445     
24446     timeout : null,
24447     
24448     hoverState : null, //???
24449     
24450     placement : 'bottom', 
24451     
24452     getAutoCreate : function(){
24453     
24454         var cfg = {
24455            cls : 'tooltip',
24456            role : 'tooltip',
24457            cn : [
24458                 {
24459                     cls : 'tooltip-arrow'
24460                 },
24461                 {
24462                     cls : 'tooltip-inner'
24463                 }
24464            ]
24465         };
24466         
24467         return cfg;
24468     },
24469     bind : function(el)
24470     {
24471         this.bindEl = el;
24472     },
24473       
24474     
24475     enter : function () {
24476        
24477         if (this.timeout != null) {
24478             clearTimeout(this.timeout);
24479         }
24480         
24481         this.hoverState = 'in';
24482          //Roo.log("enter - show");
24483         if (!this.delay || !this.delay.show) {
24484             this.show();
24485             return;
24486         }
24487         var _t = this;
24488         this.timeout = setTimeout(function () {
24489             if (_t.hoverState == 'in') {
24490                 _t.show();
24491             }
24492         }, this.delay.show);
24493     },
24494     leave : function()
24495     {
24496         clearTimeout(this.timeout);
24497     
24498         this.hoverState = 'out';
24499          if (!this.delay || !this.delay.hide) {
24500             this.hide();
24501             return;
24502         }
24503        
24504         var _t = this;
24505         this.timeout = setTimeout(function () {
24506             //Roo.log("leave - timeout");
24507             
24508             if (_t.hoverState == 'out') {
24509                 _t.hide();
24510                 Roo.bootstrap.Tooltip.currentEl = false;
24511             }
24512         }, delay);
24513     },
24514     
24515     show : function ()
24516     {
24517         if (!this.el) {
24518             this.render(document.body);
24519         }
24520         // set content.
24521         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24522         
24523         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24524         
24525         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24526         
24527         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24528         
24529         var placement = typeof this.placement == 'function' ?
24530             this.placement.call(this, this.el, on_el) :
24531             this.placement;
24532             
24533         var autoToken = /\s?auto?\s?/i;
24534         var autoPlace = autoToken.test(placement);
24535         if (autoPlace) {
24536             placement = placement.replace(autoToken, '') || 'top';
24537         }
24538         
24539         //this.el.detach()
24540         //this.el.setXY([0,0]);
24541         this.el.show();
24542         //this.el.dom.style.display='block';
24543         
24544         //this.el.appendTo(on_el);
24545         
24546         var p = this.getPosition();
24547         var box = this.el.getBox();
24548         
24549         if (autoPlace) {
24550             // fixme..
24551         }
24552         
24553         var align = Roo.bootstrap.Tooltip.alignment[placement];
24554         
24555         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24556         
24557         if(placement == 'top' || placement == 'bottom'){
24558             if(xy[0] < 0){
24559                 placement = 'right';
24560             }
24561             
24562             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24563                 placement = 'left';
24564             }
24565             
24566             var scroll = Roo.select('body', true).first().getScroll();
24567             
24568             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24569                 placement = 'top';
24570             }
24571             
24572         }
24573         
24574         align = Roo.bootstrap.Tooltip.alignment[placement];
24575         
24576         this.el.alignTo(this.bindEl, align[0],align[1]);
24577         //var arrow = this.el.select('.arrow',true).first();
24578         //arrow.set(align[2], 
24579         
24580         this.el.addClass(placement);
24581         
24582         this.el.addClass('in fade');
24583         
24584         this.hoverState = null;
24585         
24586         if (this.el.hasClass('fade')) {
24587             // fade it?
24588         }
24589         
24590     },
24591     hide : function()
24592     {
24593          
24594         if (!this.el) {
24595             return;
24596         }
24597         //this.el.setXY([0,0]);
24598         this.el.removeClass('in');
24599         //this.el.hide();
24600         
24601     }
24602     
24603 });
24604  
24605
24606  /*
24607  * - LGPL
24608  *
24609  * Location Picker
24610  * 
24611  */
24612
24613 /**
24614  * @class Roo.bootstrap.LocationPicker
24615  * @extends Roo.bootstrap.Component
24616  * Bootstrap LocationPicker class
24617  * @cfg {Number} latitude Position when init default 0
24618  * @cfg {Number} longitude Position when init default 0
24619  * @cfg {Number} zoom default 15
24620  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24621  * @cfg {Boolean} mapTypeControl default false
24622  * @cfg {Boolean} disableDoubleClickZoom default false
24623  * @cfg {Boolean} scrollwheel default true
24624  * @cfg {Boolean} streetViewControl default false
24625  * @cfg {Number} radius default 0
24626  * @cfg {String} locationName
24627  * @cfg {Boolean} draggable default true
24628  * @cfg {Boolean} enableAutocomplete default false
24629  * @cfg {Boolean} enableReverseGeocode default true
24630  * @cfg {String} markerTitle
24631  * 
24632  * @constructor
24633  * Create a new LocationPicker
24634  * @param {Object} config The config object
24635  */
24636
24637
24638 Roo.bootstrap.LocationPicker = function(config){
24639     
24640     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24641     
24642     this.addEvents({
24643         /**
24644          * @event initial
24645          * Fires when the picker initialized.
24646          * @param {Roo.bootstrap.LocationPicker} this
24647          * @param {Google Location} location
24648          */
24649         initial : true,
24650         /**
24651          * @event positionchanged
24652          * Fires when the picker position changed.
24653          * @param {Roo.bootstrap.LocationPicker} this
24654          * @param {Google Location} location
24655          */
24656         positionchanged : true,
24657         /**
24658          * @event resize
24659          * Fires when the map resize.
24660          * @param {Roo.bootstrap.LocationPicker} this
24661          */
24662         resize : true,
24663         /**
24664          * @event show
24665          * Fires when the map show.
24666          * @param {Roo.bootstrap.LocationPicker} this
24667          */
24668         show : true,
24669         /**
24670          * @event hide
24671          * Fires when the map hide.
24672          * @param {Roo.bootstrap.LocationPicker} this
24673          */
24674         hide : true,
24675         /**
24676          * @event mapClick
24677          * Fires when click the map.
24678          * @param {Roo.bootstrap.LocationPicker} this
24679          * @param {Map event} e
24680          */
24681         mapClick : true,
24682         /**
24683          * @event mapRightClick
24684          * Fires when right click the map.
24685          * @param {Roo.bootstrap.LocationPicker} this
24686          * @param {Map event} e
24687          */
24688         mapRightClick : true,
24689         /**
24690          * @event markerClick
24691          * Fires when click the marker.
24692          * @param {Roo.bootstrap.LocationPicker} this
24693          * @param {Map event} e
24694          */
24695         markerClick : true,
24696         /**
24697          * @event markerRightClick
24698          * Fires when right click the marker.
24699          * @param {Roo.bootstrap.LocationPicker} this
24700          * @param {Map event} e
24701          */
24702         markerRightClick : true,
24703         /**
24704          * @event OverlayViewDraw
24705          * Fires when OverlayView Draw
24706          * @param {Roo.bootstrap.LocationPicker} this
24707          */
24708         OverlayViewDraw : true,
24709         /**
24710          * @event OverlayViewOnAdd
24711          * Fires when OverlayView Draw
24712          * @param {Roo.bootstrap.LocationPicker} this
24713          */
24714         OverlayViewOnAdd : true,
24715         /**
24716          * @event OverlayViewOnRemove
24717          * Fires when OverlayView Draw
24718          * @param {Roo.bootstrap.LocationPicker} this
24719          */
24720         OverlayViewOnRemove : true,
24721         /**
24722          * @event OverlayViewShow
24723          * Fires when OverlayView Draw
24724          * @param {Roo.bootstrap.LocationPicker} this
24725          * @param {Pixel} cpx
24726          */
24727         OverlayViewShow : true,
24728         /**
24729          * @event OverlayViewHide
24730          * Fires when OverlayView Draw
24731          * @param {Roo.bootstrap.LocationPicker} this
24732          */
24733         OverlayViewHide : true,
24734         /**
24735          * @event loadexception
24736          * Fires when load google lib failed.
24737          * @param {Roo.bootstrap.LocationPicker} this
24738          */
24739         loadexception : true
24740     });
24741         
24742 };
24743
24744 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24745     
24746     gMapContext: false,
24747     
24748     latitude: 0,
24749     longitude: 0,
24750     zoom: 15,
24751     mapTypeId: false,
24752     mapTypeControl: false,
24753     disableDoubleClickZoom: false,
24754     scrollwheel: true,
24755     streetViewControl: false,
24756     radius: 0,
24757     locationName: '',
24758     draggable: true,
24759     enableAutocomplete: false,
24760     enableReverseGeocode: true,
24761     markerTitle: '',
24762     
24763     getAutoCreate: function()
24764     {
24765
24766         var cfg = {
24767             tag: 'div',
24768             cls: 'roo-location-picker'
24769         };
24770         
24771         return cfg
24772     },
24773     
24774     initEvents: function(ct, position)
24775     {       
24776         if(!this.el.getWidth() || this.isApplied()){
24777             return;
24778         }
24779         
24780         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24781         
24782         this.initial();
24783     },
24784     
24785     initial: function()
24786     {
24787         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24788             this.fireEvent('loadexception', this);
24789             return;
24790         }
24791         
24792         if(!this.mapTypeId){
24793             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24794         }
24795         
24796         this.gMapContext = this.GMapContext();
24797         
24798         this.initOverlayView();
24799         
24800         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24801         
24802         var _this = this;
24803                 
24804         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24805             _this.setPosition(_this.gMapContext.marker.position);
24806         });
24807         
24808         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24809             _this.fireEvent('mapClick', this, event);
24810             
24811         });
24812
24813         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24814             _this.fireEvent('mapRightClick', this, event);
24815             
24816         });
24817         
24818         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24819             _this.fireEvent('markerClick', this, event);
24820             
24821         });
24822
24823         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24824             _this.fireEvent('markerRightClick', this, event);
24825             
24826         });
24827         
24828         this.setPosition(this.gMapContext.location);
24829         
24830         this.fireEvent('initial', this, this.gMapContext.location);
24831     },
24832     
24833     initOverlayView: function()
24834     {
24835         var _this = this;
24836         
24837         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24838             
24839             draw: function()
24840             {
24841                 _this.fireEvent('OverlayViewDraw', _this);
24842             },
24843             
24844             onAdd: function()
24845             {
24846                 _this.fireEvent('OverlayViewOnAdd', _this);
24847             },
24848             
24849             onRemove: function()
24850             {
24851                 _this.fireEvent('OverlayViewOnRemove', _this);
24852             },
24853             
24854             show: function(cpx)
24855             {
24856                 _this.fireEvent('OverlayViewShow', _this, cpx);
24857             },
24858             
24859             hide: function()
24860             {
24861                 _this.fireEvent('OverlayViewHide', _this);
24862             }
24863             
24864         });
24865     },
24866     
24867     fromLatLngToContainerPixel: function(event)
24868     {
24869         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24870     },
24871     
24872     isApplied: function() 
24873     {
24874         return this.getGmapContext() == false ? false : true;
24875     },
24876     
24877     getGmapContext: function() 
24878     {
24879         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24880     },
24881     
24882     GMapContext: function() 
24883     {
24884         var position = new google.maps.LatLng(this.latitude, this.longitude);
24885         
24886         var _map = new google.maps.Map(this.el.dom, {
24887             center: position,
24888             zoom: this.zoom,
24889             mapTypeId: this.mapTypeId,
24890             mapTypeControl: this.mapTypeControl,
24891             disableDoubleClickZoom: this.disableDoubleClickZoom,
24892             scrollwheel: this.scrollwheel,
24893             streetViewControl: this.streetViewControl,
24894             locationName: this.locationName,
24895             draggable: this.draggable,
24896             enableAutocomplete: this.enableAutocomplete,
24897             enableReverseGeocode: this.enableReverseGeocode
24898         });
24899         
24900         var _marker = new google.maps.Marker({
24901             position: position,
24902             map: _map,
24903             title: this.markerTitle,
24904             draggable: this.draggable
24905         });
24906         
24907         return {
24908             map: _map,
24909             marker: _marker,
24910             circle: null,
24911             location: position,
24912             radius: this.radius,
24913             locationName: this.locationName,
24914             addressComponents: {
24915                 formatted_address: null,
24916                 addressLine1: null,
24917                 addressLine2: null,
24918                 streetName: null,
24919                 streetNumber: null,
24920                 city: null,
24921                 district: null,
24922                 state: null,
24923                 stateOrProvince: null
24924             },
24925             settings: this,
24926             domContainer: this.el.dom,
24927             geodecoder: new google.maps.Geocoder()
24928         };
24929     },
24930     
24931     drawCircle: function(center, radius, options) 
24932     {
24933         if (this.gMapContext.circle != null) {
24934             this.gMapContext.circle.setMap(null);
24935         }
24936         if (radius > 0) {
24937             radius *= 1;
24938             options = Roo.apply({}, options, {
24939                 strokeColor: "#0000FF",
24940                 strokeOpacity: .35,
24941                 strokeWeight: 2,
24942                 fillColor: "#0000FF",
24943                 fillOpacity: .2
24944             });
24945             
24946             options.map = this.gMapContext.map;
24947             options.radius = radius;
24948             options.center = center;
24949             this.gMapContext.circle = new google.maps.Circle(options);
24950             return this.gMapContext.circle;
24951         }
24952         
24953         return null;
24954     },
24955     
24956     setPosition: function(location) 
24957     {
24958         this.gMapContext.location = location;
24959         this.gMapContext.marker.setPosition(location);
24960         this.gMapContext.map.panTo(location);
24961         this.drawCircle(location, this.gMapContext.radius, {});
24962         
24963         var _this = this;
24964         
24965         if (this.gMapContext.settings.enableReverseGeocode) {
24966             this.gMapContext.geodecoder.geocode({
24967                 latLng: this.gMapContext.location
24968             }, function(results, status) {
24969                 
24970                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24971                     _this.gMapContext.locationName = results[0].formatted_address;
24972                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24973                     
24974                     _this.fireEvent('positionchanged', this, location);
24975                 }
24976             });
24977             
24978             return;
24979         }
24980         
24981         this.fireEvent('positionchanged', this, location);
24982     },
24983     
24984     resize: function()
24985     {
24986         google.maps.event.trigger(this.gMapContext.map, "resize");
24987         
24988         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24989         
24990         this.fireEvent('resize', this);
24991     },
24992     
24993     setPositionByLatLng: function(latitude, longitude)
24994     {
24995         this.setPosition(new google.maps.LatLng(latitude, longitude));
24996     },
24997     
24998     getCurrentPosition: function() 
24999     {
25000         return {
25001             latitude: this.gMapContext.location.lat(),
25002             longitude: this.gMapContext.location.lng()
25003         };
25004     },
25005     
25006     getAddressName: function() 
25007     {
25008         return this.gMapContext.locationName;
25009     },
25010     
25011     getAddressComponents: function() 
25012     {
25013         return this.gMapContext.addressComponents;
25014     },
25015     
25016     address_component_from_google_geocode: function(address_components) 
25017     {
25018         var result = {};
25019         
25020         for (var i = 0; i < address_components.length; i++) {
25021             var component = address_components[i];
25022             if (component.types.indexOf("postal_code") >= 0) {
25023                 result.postalCode = component.short_name;
25024             } else if (component.types.indexOf("street_number") >= 0) {
25025                 result.streetNumber = component.short_name;
25026             } else if (component.types.indexOf("route") >= 0) {
25027                 result.streetName = component.short_name;
25028             } else if (component.types.indexOf("neighborhood") >= 0) {
25029                 result.city = component.short_name;
25030             } else if (component.types.indexOf("locality") >= 0) {
25031                 result.city = component.short_name;
25032             } else if (component.types.indexOf("sublocality") >= 0) {
25033                 result.district = component.short_name;
25034             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25035                 result.stateOrProvince = component.short_name;
25036             } else if (component.types.indexOf("country") >= 0) {
25037                 result.country = component.short_name;
25038             }
25039         }
25040         
25041         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25042         result.addressLine2 = "";
25043         return result;
25044     },
25045     
25046     setZoomLevel: function(zoom)
25047     {
25048         this.gMapContext.map.setZoom(zoom);
25049     },
25050     
25051     show: function()
25052     {
25053         if(!this.el){
25054             return;
25055         }
25056         
25057         this.el.show();
25058         
25059         this.resize();
25060         
25061         this.fireEvent('show', this);
25062     },
25063     
25064     hide: function()
25065     {
25066         if(!this.el){
25067             return;
25068         }
25069         
25070         this.el.hide();
25071         
25072         this.fireEvent('hide', this);
25073     }
25074     
25075 });
25076
25077 Roo.apply(Roo.bootstrap.LocationPicker, {
25078     
25079     OverlayView : function(map, options)
25080     {
25081         options = options || {};
25082         
25083         this.setMap(map);
25084     }
25085     
25086     
25087 });/*
25088  * - LGPL
25089  *
25090  * Alert
25091  * 
25092  */
25093
25094 /**
25095  * @class Roo.bootstrap.Alert
25096  * @extends Roo.bootstrap.Component
25097  * Bootstrap Alert class
25098  * @cfg {String} title The title of alert
25099  * @cfg {String} html The content of alert
25100  * @cfg {String} weight (  success | info | warning | danger )
25101  * @cfg {String} faicon font-awesomeicon
25102  * 
25103  * @constructor
25104  * Create a new alert
25105  * @param {Object} config The config object
25106  */
25107
25108
25109 Roo.bootstrap.Alert = function(config){
25110     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25111     
25112 };
25113
25114 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25115     
25116     title: '',
25117     html: '',
25118     weight: false,
25119     faicon: false,
25120     
25121     getAutoCreate : function()
25122     {
25123         
25124         var cfg = {
25125             tag : 'div',
25126             cls : 'alert',
25127             cn : [
25128                 {
25129                     tag : 'i',
25130                     cls : 'roo-alert-icon'
25131                     
25132                 },
25133                 {
25134                     tag : 'b',
25135                     cls : 'roo-alert-title',
25136                     html : this.title
25137                 },
25138                 {
25139                     tag : 'span',
25140                     cls : 'roo-alert-text',
25141                     html : this.html
25142                 }
25143             ]
25144         };
25145         
25146         if(this.faicon){
25147             cfg.cn[0].cls += ' fa ' + this.faicon;
25148         }
25149         
25150         if(this.weight){
25151             cfg.cls += ' alert-' + this.weight;
25152         }
25153         
25154         return cfg;
25155     },
25156     
25157     initEvents: function() 
25158     {
25159         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25160     },
25161     
25162     setTitle : function(str)
25163     {
25164         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25165     },
25166     
25167     setText : function(str)
25168     {
25169         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25170     },
25171     
25172     setWeight : function(weight)
25173     {
25174         if(this.weight){
25175             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25176         }
25177         
25178         this.weight = weight;
25179         
25180         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25181     },
25182     
25183     setIcon : function(icon)
25184     {
25185         if(this.faicon){
25186             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25187         }
25188         
25189         this.faicon = icon;
25190         
25191         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25192     },
25193     
25194     hide: function() 
25195     {
25196         this.el.hide();   
25197     },
25198     
25199     show: function() 
25200     {  
25201         this.el.show();   
25202     }
25203     
25204 });
25205
25206  
25207 /*
25208 * Licence: LGPL
25209 */
25210
25211 /**
25212  * @class Roo.bootstrap.UploadCropbox
25213  * @extends Roo.bootstrap.Component
25214  * Bootstrap UploadCropbox class
25215  * @cfg {String} emptyText show when image has been loaded
25216  * @cfg {String} rotateNotify show when image too small to rotate
25217  * @cfg {Number} errorTimeout default 3000
25218  * @cfg {Number} minWidth default 300
25219  * @cfg {Number} minHeight default 300
25220  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25221  * @cfg {Boolean} isDocument (true|false) default false
25222  * @cfg {String} url action url
25223  * @cfg {String} paramName default 'imageUpload'
25224  * @cfg {String} method default POST
25225  * @cfg {Boolean} loadMask (true|false) default true
25226  * @cfg {Boolean} loadingText default 'Loading...'
25227  * 
25228  * @constructor
25229  * Create a new UploadCropbox
25230  * @param {Object} config The config object
25231  */
25232
25233 Roo.bootstrap.UploadCropbox = function(config){
25234     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25235     
25236     this.addEvents({
25237         /**
25238          * @event beforeselectfile
25239          * Fire before select file
25240          * @param {Roo.bootstrap.UploadCropbox} this
25241          */
25242         "beforeselectfile" : true,
25243         /**
25244          * @event initial
25245          * Fire after initEvent
25246          * @param {Roo.bootstrap.UploadCropbox} this
25247          */
25248         "initial" : true,
25249         /**
25250          * @event crop
25251          * Fire after initEvent
25252          * @param {Roo.bootstrap.UploadCropbox} this
25253          * @param {String} data
25254          */
25255         "crop" : true,
25256         /**
25257          * @event prepare
25258          * Fire when preparing the file data
25259          * @param {Roo.bootstrap.UploadCropbox} this
25260          * @param {Object} file
25261          */
25262         "prepare" : true,
25263         /**
25264          * @event exception
25265          * Fire when get exception
25266          * @param {Roo.bootstrap.UploadCropbox} this
25267          * @param {XMLHttpRequest} xhr
25268          */
25269         "exception" : true,
25270         /**
25271          * @event beforeloadcanvas
25272          * Fire before load the canvas
25273          * @param {Roo.bootstrap.UploadCropbox} this
25274          * @param {String} src
25275          */
25276         "beforeloadcanvas" : true,
25277         /**
25278          * @event trash
25279          * Fire when trash image
25280          * @param {Roo.bootstrap.UploadCropbox} this
25281          */
25282         "trash" : true,
25283         /**
25284          * @event download
25285          * Fire when download the image
25286          * @param {Roo.bootstrap.UploadCropbox} this
25287          */
25288         "download" : true,
25289         /**
25290          * @event footerbuttonclick
25291          * Fire when footerbuttonclick
25292          * @param {Roo.bootstrap.UploadCropbox} this
25293          * @param {String} type
25294          */
25295         "footerbuttonclick" : true,
25296         /**
25297          * @event resize
25298          * Fire when resize
25299          * @param {Roo.bootstrap.UploadCropbox} this
25300          */
25301         "resize" : true,
25302         /**
25303          * @event rotate
25304          * Fire when rotate the image
25305          * @param {Roo.bootstrap.UploadCropbox} this
25306          * @param {String} pos
25307          */
25308         "rotate" : true,
25309         /**
25310          * @event inspect
25311          * Fire when inspect the file
25312          * @param {Roo.bootstrap.UploadCropbox} this
25313          * @param {Object} file
25314          */
25315         "inspect" : true,
25316         /**
25317          * @event upload
25318          * Fire when xhr upload the file
25319          * @param {Roo.bootstrap.UploadCropbox} this
25320          * @param {Object} data
25321          */
25322         "upload" : true,
25323         /**
25324          * @event arrange
25325          * Fire when arrange the file data
25326          * @param {Roo.bootstrap.UploadCropbox} this
25327          * @param {Object} formData
25328          */
25329         "arrange" : true
25330     });
25331     
25332     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25333 };
25334
25335 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25336     
25337     emptyText : 'Click to upload image',
25338     rotateNotify : 'Image is too small to rotate',
25339     errorTimeout : 3000,
25340     scale : 0,
25341     baseScale : 1,
25342     rotate : 0,
25343     dragable : false,
25344     pinching : false,
25345     mouseX : 0,
25346     mouseY : 0,
25347     cropData : false,
25348     minWidth : 300,
25349     minHeight : 300,
25350     file : false,
25351     exif : {},
25352     baseRotate : 1,
25353     cropType : 'image/jpeg',
25354     buttons : false,
25355     canvasLoaded : false,
25356     isDocument : false,
25357     method : 'POST',
25358     paramName : 'imageUpload',
25359     loadMask : true,
25360     loadingText : 'Loading...',
25361     maskEl : false,
25362     
25363     getAutoCreate : function()
25364     {
25365         var cfg = {
25366             tag : 'div',
25367             cls : 'roo-upload-cropbox',
25368             cn : [
25369                 {
25370                     tag : 'input',
25371                     cls : 'roo-upload-cropbox-selector',
25372                     type : 'file'
25373                 },
25374                 {
25375                     tag : 'div',
25376                     cls : 'roo-upload-cropbox-body',
25377                     style : 'cursor:pointer',
25378                     cn : [
25379                         {
25380                             tag : 'div',
25381                             cls : 'roo-upload-cropbox-preview'
25382                         },
25383                         {
25384                             tag : 'div',
25385                             cls : 'roo-upload-cropbox-thumb'
25386                         },
25387                         {
25388                             tag : 'div',
25389                             cls : 'roo-upload-cropbox-empty-notify',
25390                             html : this.emptyText
25391                         },
25392                         {
25393                             tag : 'div',
25394                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25395                             html : this.rotateNotify
25396                         }
25397                     ]
25398                 },
25399                 {
25400                     tag : 'div',
25401                     cls : 'roo-upload-cropbox-footer',
25402                     cn : {
25403                         tag : 'div',
25404                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25405                         cn : []
25406                     }
25407                 }
25408             ]
25409         };
25410         
25411         return cfg;
25412     },
25413     
25414     onRender : function(ct, position)
25415     {
25416         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25417         
25418         if (this.buttons.length) {
25419             
25420             Roo.each(this.buttons, function(bb) {
25421                 
25422                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25423                 
25424                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25425                 
25426             }, this);
25427         }
25428         
25429         if(this.loadMask){
25430             this.maskEl = this.el;
25431         }
25432     },
25433     
25434     initEvents : function()
25435     {
25436         this.urlAPI = (window.createObjectURL && window) || 
25437                                 (window.URL && URL.revokeObjectURL && URL) || 
25438                                 (window.webkitURL && webkitURL);
25439                         
25440         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25441         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25442         
25443         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25444         this.selectorEl.hide();
25445         
25446         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25447         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25448         
25449         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25450         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25451         this.thumbEl.hide();
25452         
25453         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25454         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25455         
25456         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25457         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25458         this.errorEl.hide();
25459         
25460         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25461         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25462         this.footerEl.hide();
25463         
25464         this.setThumbBoxSize();
25465         
25466         this.bind();
25467         
25468         this.resize();
25469         
25470         this.fireEvent('initial', this);
25471     },
25472
25473     bind : function()
25474     {
25475         var _this = this;
25476         
25477         window.addEventListener("resize", function() { _this.resize(); } );
25478         
25479         this.bodyEl.on('click', this.beforeSelectFile, this);
25480         
25481         if(Roo.isTouch){
25482             this.bodyEl.on('touchstart', this.onTouchStart, this);
25483             this.bodyEl.on('touchmove', this.onTouchMove, this);
25484             this.bodyEl.on('touchend', this.onTouchEnd, this);
25485         }
25486         
25487         if(!Roo.isTouch){
25488             this.bodyEl.on('mousedown', this.onMouseDown, this);
25489             this.bodyEl.on('mousemove', this.onMouseMove, this);
25490             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25491             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25492             Roo.get(document).on('mouseup', this.onMouseUp, this);
25493         }
25494         
25495         this.selectorEl.on('change', this.onFileSelected, this);
25496     },
25497     
25498     reset : function()
25499     {    
25500         this.scale = 0;
25501         this.baseScale = 1;
25502         this.rotate = 0;
25503         this.baseRotate = 1;
25504         this.dragable = false;
25505         this.pinching = false;
25506         this.mouseX = 0;
25507         this.mouseY = 0;
25508         this.cropData = false;
25509         this.notifyEl.dom.innerHTML = this.emptyText;
25510         
25511         this.selectorEl.dom.value = '';
25512         
25513     },
25514     
25515     resize : function()
25516     {
25517         if(this.fireEvent('resize', this) != false){
25518             this.setThumbBoxPosition();
25519             this.setCanvasPosition();
25520         }
25521     },
25522     
25523     onFooterButtonClick : function(e, el, o, type)
25524     {
25525         switch (type) {
25526             case 'rotate-left' :
25527                 this.onRotateLeft(e);
25528                 break;
25529             case 'rotate-right' :
25530                 this.onRotateRight(e);
25531                 break;
25532             case 'picture' :
25533                 this.beforeSelectFile(e);
25534                 break;
25535             case 'trash' :
25536                 this.trash(e);
25537                 break;
25538             case 'crop' :
25539                 this.crop(e);
25540                 break;
25541             case 'download' :
25542                 this.download(e);
25543                 break;
25544             default :
25545                 break;
25546         }
25547         
25548         this.fireEvent('footerbuttonclick', this, type);
25549     },
25550     
25551     beforeSelectFile : function(e)
25552     {
25553         e.preventDefault();
25554         
25555         if(this.fireEvent('beforeselectfile', this) != false){
25556             this.selectorEl.dom.click();
25557         }
25558     },
25559     
25560     onFileSelected : function(e)
25561     {
25562         e.preventDefault();
25563         
25564         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25565             return;
25566         }
25567         
25568         var file = this.selectorEl.dom.files[0];
25569         
25570         if(this.fireEvent('inspect', this, file) != false){
25571             this.prepare(file);
25572         }
25573         
25574     },
25575     
25576     trash : function(e)
25577     {
25578         this.fireEvent('trash', this);
25579     },
25580     
25581     download : function(e)
25582     {
25583         this.fireEvent('download', this);
25584     },
25585     
25586     loadCanvas : function(src)
25587     {   
25588         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25589             
25590             this.reset();
25591             
25592             this.imageEl = document.createElement('img');
25593             
25594             var _this = this;
25595             
25596             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25597             
25598             this.imageEl.src = src;
25599         }
25600     },
25601     
25602     onLoadCanvas : function()
25603     {   
25604         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25605         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25606         
25607         this.bodyEl.un('click', this.beforeSelectFile, this);
25608         
25609         this.notifyEl.hide();
25610         this.thumbEl.show();
25611         this.footerEl.show();
25612         
25613         this.baseRotateLevel();
25614         
25615         if(this.isDocument){
25616             this.setThumbBoxSize();
25617         }
25618         
25619         this.setThumbBoxPosition();
25620         
25621         this.baseScaleLevel();
25622         
25623         this.draw();
25624         
25625         this.resize();
25626         
25627         this.canvasLoaded = true;
25628         
25629         if(this.loadMask){
25630             this.maskEl.unmask();
25631         }
25632         
25633     },
25634     
25635     setCanvasPosition : function()
25636     {   
25637         if(!this.canvasEl){
25638             return;
25639         }
25640         
25641         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25642         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25643         
25644         this.previewEl.setLeft(pw);
25645         this.previewEl.setTop(ph);
25646         
25647     },
25648     
25649     onMouseDown : function(e)
25650     {   
25651         e.stopEvent();
25652         
25653         this.dragable = true;
25654         this.pinching = false;
25655         
25656         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25657             this.dragable = false;
25658             return;
25659         }
25660         
25661         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25662         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25663         
25664     },
25665     
25666     onMouseMove : function(e)
25667     {   
25668         e.stopEvent();
25669         
25670         if(!this.canvasLoaded){
25671             return;
25672         }
25673         
25674         if (!this.dragable){
25675             return;
25676         }
25677         
25678         var minX = Math.ceil(this.thumbEl.getLeft(true));
25679         var minY = Math.ceil(this.thumbEl.getTop(true));
25680         
25681         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25682         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25683         
25684         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25685         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25686         
25687         x = x - this.mouseX;
25688         y = y - this.mouseY;
25689         
25690         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25691         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25692         
25693         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25694         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25695         
25696         this.previewEl.setLeft(bgX);
25697         this.previewEl.setTop(bgY);
25698         
25699         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25700         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25701     },
25702     
25703     onMouseUp : function(e)
25704     {   
25705         e.stopEvent();
25706         
25707         this.dragable = false;
25708     },
25709     
25710     onMouseWheel : function(e)
25711     {   
25712         e.stopEvent();
25713         
25714         this.startScale = this.scale;
25715         
25716         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25717         
25718         if(!this.zoomable()){
25719             this.scale = this.startScale;
25720             return;
25721         }
25722         
25723         this.draw();
25724         
25725         return;
25726     },
25727     
25728     zoomable : function()
25729     {
25730         var minScale = this.thumbEl.getWidth() / this.minWidth;
25731         
25732         if(this.minWidth < this.minHeight){
25733             minScale = this.thumbEl.getHeight() / this.minHeight;
25734         }
25735         
25736         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25737         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25738         
25739         if(
25740                 this.isDocument &&
25741                 (this.rotate == 0 || this.rotate == 180) && 
25742                 (
25743                     width > this.imageEl.OriginWidth || 
25744                     height > this.imageEl.OriginHeight ||
25745                     (width < this.minWidth && height < this.minHeight)
25746                 )
25747         ){
25748             return false;
25749         }
25750         
25751         if(
25752                 this.isDocument &&
25753                 (this.rotate == 90 || this.rotate == 270) && 
25754                 (
25755                     width > this.imageEl.OriginWidth || 
25756                     height > this.imageEl.OriginHeight ||
25757                     (width < this.minHeight && height < this.minWidth)
25758                 )
25759         ){
25760             return false;
25761         }
25762         
25763         if(
25764                 !this.isDocument &&
25765                 (this.rotate == 0 || this.rotate == 180) && 
25766                 (
25767                     width < this.minWidth || 
25768                     width > this.imageEl.OriginWidth || 
25769                     height < this.minHeight || 
25770                     height > this.imageEl.OriginHeight
25771                 )
25772         ){
25773             return false;
25774         }
25775         
25776         if(
25777                 !this.isDocument &&
25778                 (this.rotate == 90 || this.rotate == 270) && 
25779                 (
25780                     width < this.minHeight || 
25781                     width > this.imageEl.OriginWidth || 
25782                     height < this.minWidth || 
25783                     height > this.imageEl.OriginHeight
25784                 )
25785         ){
25786             return false;
25787         }
25788         
25789         return true;
25790         
25791     },
25792     
25793     onRotateLeft : function(e)
25794     {   
25795         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25796             
25797             var minScale = this.thumbEl.getWidth() / this.minWidth;
25798             
25799             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25800             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25801             
25802             this.startScale = this.scale;
25803             
25804             while (this.getScaleLevel() < minScale){
25805             
25806                 this.scale = this.scale + 1;
25807                 
25808                 if(!this.zoomable()){
25809                     break;
25810                 }
25811                 
25812                 if(
25813                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25814                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25815                 ){
25816                     continue;
25817                 }
25818                 
25819                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25820
25821                 this.draw();
25822                 
25823                 return;
25824             }
25825             
25826             this.scale = this.startScale;
25827             
25828             this.onRotateFail();
25829             
25830             return false;
25831         }
25832         
25833         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25834
25835         if(this.isDocument){
25836             this.setThumbBoxSize();
25837             this.setThumbBoxPosition();
25838             this.setCanvasPosition();
25839         }
25840         
25841         this.draw();
25842         
25843         this.fireEvent('rotate', this, 'left');
25844         
25845     },
25846     
25847     onRotateRight : function(e)
25848     {
25849         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25850             
25851             var minScale = this.thumbEl.getWidth() / this.minWidth;
25852         
25853             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25854             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25855             
25856             this.startScale = this.scale;
25857             
25858             while (this.getScaleLevel() < minScale){
25859             
25860                 this.scale = this.scale + 1;
25861                 
25862                 if(!this.zoomable()){
25863                     break;
25864                 }
25865                 
25866                 if(
25867                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25868                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25869                 ){
25870                     continue;
25871                 }
25872                 
25873                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25874
25875                 this.draw();
25876                 
25877                 return;
25878             }
25879             
25880             this.scale = this.startScale;
25881             
25882             this.onRotateFail();
25883             
25884             return false;
25885         }
25886         
25887         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25888
25889         if(this.isDocument){
25890             this.setThumbBoxSize();
25891             this.setThumbBoxPosition();
25892             this.setCanvasPosition();
25893         }
25894         
25895         this.draw();
25896         
25897         this.fireEvent('rotate', this, 'right');
25898     },
25899     
25900     onRotateFail : function()
25901     {
25902         this.errorEl.show(true);
25903         
25904         var _this = this;
25905         
25906         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25907     },
25908     
25909     draw : function()
25910     {
25911         this.previewEl.dom.innerHTML = '';
25912         
25913         var canvasEl = document.createElement("canvas");
25914         
25915         var contextEl = canvasEl.getContext("2d");
25916         
25917         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25918         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25919         var center = this.imageEl.OriginWidth / 2;
25920         
25921         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25922             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25923             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25924             center = this.imageEl.OriginHeight / 2;
25925         }
25926         
25927         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25928         
25929         contextEl.translate(center, center);
25930         contextEl.rotate(this.rotate * Math.PI / 180);
25931
25932         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25933         
25934         this.canvasEl = document.createElement("canvas");
25935         
25936         this.contextEl = this.canvasEl.getContext("2d");
25937         
25938         switch (this.rotate) {
25939             case 0 :
25940                 
25941                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25942                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25943                 
25944                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25945                 
25946                 break;
25947             case 90 : 
25948                 
25949                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25950                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25951                 
25952                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25953                     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);
25954                     break;
25955                 }
25956                 
25957                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25958                 
25959                 break;
25960             case 180 :
25961                 
25962                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25963                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25964                 
25965                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25966                     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);
25967                     break;
25968                 }
25969                 
25970                 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);
25971                 
25972                 break;
25973             case 270 :
25974                 
25975                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25976                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25977         
25978                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25979                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25980                     break;
25981                 }
25982                 
25983                 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);
25984                 
25985                 break;
25986             default : 
25987                 break;
25988         }
25989         
25990         this.previewEl.appendChild(this.canvasEl);
25991         
25992         this.setCanvasPosition();
25993     },
25994     
25995     crop : function()
25996     {
25997         if(!this.canvasLoaded){
25998             return;
25999         }
26000         
26001         var imageCanvas = document.createElement("canvas");
26002         
26003         var imageContext = imageCanvas.getContext("2d");
26004         
26005         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26006         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26007         
26008         var center = imageCanvas.width / 2;
26009         
26010         imageContext.translate(center, center);
26011         
26012         imageContext.rotate(this.rotate * Math.PI / 180);
26013         
26014         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26015         
26016         var canvas = document.createElement("canvas");
26017         
26018         var context = canvas.getContext("2d");
26019                 
26020         canvas.width = this.minWidth;
26021         canvas.height = this.minHeight;
26022
26023         switch (this.rotate) {
26024             case 0 :
26025                 
26026                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26027                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26028                 
26029                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26030                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26031                 
26032                 var targetWidth = this.minWidth - 2 * x;
26033                 var targetHeight = this.minHeight - 2 * y;
26034                 
26035                 var scale = 1;
26036                 
26037                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26038                     scale = targetWidth / width;
26039                 }
26040                 
26041                 if(x > 0 && y == 0){
26042                     scale = targetHeight / height;
26043                 }
26044                 
26045                 if(x > 0 && y > 0){
26046                     scale = targetWidth / width;
26047                     
26048                     if(width < height){
26049                         scale = targetHeight / height;
26050                     }
26051                 }
26052                 
26053                 context.scale(scale, scale);
26054                 
26055                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26056                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26057
26058                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26059                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26060
26061                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26062                 
26063                 break;
26064             case 90 : 
26065                 
26066                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26067                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26068                 
26069                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26070                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26071                 
26072                 var targetWidth = this.minWidth - 2 * x;
26073                 var targetHeight = this.minHeight - 2 * y;
26074                 
26075                 var scale = 1;
26076                 
26077                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26078                     scale = targetWidth / width;
26079                 }
26080                 
26081                 if(x > 0 && y == 0){
26082                     scale = targetHeight / height;
26083                 }
26084                 
26085                 if(x > 0 && y > 0){
26086                     scale = targetWidth / width;
26087                     
26088                     if(width < height){
26089                         scale = targetHeight / height;
26090                     }
26091                 }
26092                 
26093                 context.scale(scale, scale);
26094                 
26095                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26096                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26097
26098                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26099                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26100                 
26101                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26102                 
26103                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26104                 
26105                 break;
26106             case 180 :
26107                 
26108                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26109                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26110                 
26111                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26112                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26113                 
26114                 var targetWidth = this.minWidth - 2 * x;
26115                 var targetHeight = this.minHeight - 2 * y;
26116                 
26117                 var scale = 1;
26118                 
26119                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26120                     scale = targetWidth / width;
26121                 }
26122                 
26123                 if(x > 0 && y == 0){
26124                     scale = targetHeight / height;
26125                 }
26126                 
26127                 if(x > 0 && y > 0){
26128                     scale = targetWidth / width;
26129                     
26130                     if(width < height){
26131                         scale = targetHeight / height;
26132                     }
26133                 }
26134                 
26135                 context.scale(scale, scale);
26136                 
26137                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26138                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26139
26140                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26141                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26142
26143                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26144                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26145                 
26146                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26147                 
26148                 break;
26149             case 270 :
26150                 
26151                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26152                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26153                 
26154                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26155                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26156                 
26157                 var targetWidth = this.minWidth - 2 * x;
26158                 var targetHeight = this.minHeight - 2 * y;
26159                 
26160                 var scale = 1;
26161                 
26162                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26163                     scale = targetWidth / width;
26164                 }
26165                 
26166                 if(x > 0 && y == 0){
26167                     scale = targetHeight / height;
26168                 }
26169                 
26170                 if(x > 0 && y > 0){
26171                     scale = targetWidth / width;
26172                     
26173                     if(width < height){
26174                         scale = targetHeight / height;
26175                     }
26176                 }
26177                 
26178                 context.scale(scale, scale);
26179                 
26180                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26181                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26182
26183                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26184                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26185                 
26186                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26187                 
26188                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26189                 
26190                 break;
26191             default : 
26192                 break;
26193         }
26194         
26195         this.cropData = canvas.toDataURL(this.cropType);
26196         
26197         if(this.fireEvent('crop', this, this.cropData) !== false){
26198             this.process(this.file, this.cropData);
26199         }
26200         
26201         return;
26202         
26203     },
26204     
26205     setThumbBoxSize : function()
26206     {
26207         var width, height;
26208         
26209         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26210             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26211             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26212             
26213             this.minWidth = width;
26214             this.minHeight = height;
26215             
26216             if(this.rotate == 90 || this.rotate == 270){
26217                 this.minWidth = height;
26218                 this.minHeight = width;
26219             }
26220         }
26221         
26222         height = 300;
26223         width = Math.ceil(this.minWidth * height / this.minHeight);
26224         
26225         if(this.minWidth > this.minHeight){
26226             width = 300;
26227             height = Math.ceil(this.minHeight * width / this.minWidth);
26228         }
26229         
26230         this.thumbEl.setStyle({
26231             width : width + 'px',
26232             height : height + 'px'
26233         });
26234
26235         return;
26236             
26237     },
26238     
26239     setThumbBoxPosition : function()
26240     {
26241         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26242         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26243         
26244         this.thumbEl.setLeft(x);
26245         this.thumbEl.setTop(y);
26246         
26247     },
26248     
26249     baseRotateLevel : function()
26250     {
26251         this.baseRotate = 1;
26252         
26253         if(
26254                 typeof(this.exif) != 'undefined' &&
26255                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26256                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26257         ){
26258             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26259         }
26260         
26261         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26262         
26263     },
26264     
26265     baseScaleLevel : function()
26266     {
26267         var width, height;
26268         
26269         if(this.isDocument){
26270             
26271             if(this.baseRotate == 6 || this.baseRotate == 8){
26272             
26273                 height = this.thumbEl.getHeight();
26274                 this.baseScale = height / this.imageEl.OriginWidth;
26275
26276                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26277                     width = this.thumbEl.getWidth();
26278                     this.baseScale = width / this.imageEl.OriginHeight;
26279                 }
26280
26281                 return;
26282             }
26283
26284             height = this.thumbEl.getHeight();
26285             this.baseScale = height / this.imageEl.OriginHeight;
26286
26287             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26288                 width = this.thumbEl.getWidth();
26289                 this.baseScale = width / this.imageEl.OriginWidth;
26290             }
26291
26292             return;
26293         }
26294         
26295         if(this.baseRotate == 6 || this.baseRotate == 8){
26296             
26297             width = this.thumbEl.getHeight();
26298             this.baseScale = width / this.imageEl.OriginHeight;
26299             
26300             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26301                 height = this.thumbEl.getWidth();
26302                 this.baseScale = height / this.imageEl.OriginHeight;
26303             }
26304             
26305             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26306                 height = this.thumbEl.getWidth();
26307                 this.baseScale = height / this.imageEl.OriginHeight;
26308                 
26309                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26310                     width = this.thumbEl.getHeight();
26311                     this.baseScale = width / this.imageEl.OriginWidth;
26312                 }
26313             }
26314             
26315             return;
26316         }
26317         
26318         width = this.thumbEl.getWidth();
26319         this.baseScale = width / this.imageEl.OriginWidth;
26320         
26321         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26322             height = this.thumbEl.getHeight();
26323             this.baseScale = height / this.imageEl.OriginHeight;
26324         }
26325         
26326         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26327             
26328             height = this.thumbEl.getHeight();
26329             this.baseScale = height / this.imageEl.OriginHeight;
26330             
26331             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26332                 width = this.thumbEl.getWidth();
26333                 this.baseScale = width / this.imageEl.OriginWidth;
26334             }
26335             
26336         }
26337         
26338         return;
26339     },
26340     
26341     getScaleLevel : function()
26342     {
26343         return this.baseScale * Math.pow(1.1, this.scale);
26344     },
26345     
26346     onTouchStart : function(e)
26347     {
26348         if(!this.canvasLoaded){
26349             this.beforeSelectFile(e);
26350             return;
26351         }
26352         
26353         var touches = e.browserEvent.touches;
26354         
26355         if(!touches){
26356             return;
26357         }
26358         
26359         if(touches.length == 1){
26360             this.onMouseDown(e);
26361             return;
26362         }
26363         
26364         if(touches.length != 2){
26365             return;
26366         }
26367         
26368         var coords = [];
26369         
26370         for(var i = 0, finger; finger = touches[i]; i++){
26371             coords.push(finger.pageX, finger.pageY);
26372         }
26373         
26374         var x = Math.pow(coords[0] - coords[2], 2);
26375         var y = Math.pow(coords[1] - coords[3], 2);
26376         
26377         this.startDistance = Math.sqrt(x + y);
26378         
26379         this.startScale = this.scale;
26380         
26381         this.pinching = true;
26382         this.dragable = false;
26383         
26384     },
26385     
26386     onTouchMove : function(e)
26387     {
26388         if(!this.pinching && !this.dragable){
26389             return;
26390         }
26391         
26392         var touches = e.browserEvent.touches;
26393         
26394         if(!touches){
26395             return;
26396         }
26397         
26398         if(this.dragable){
26399             this.onMouseMove(e);
26400             return;
26401         }
26402         
26403         var coords = [];
26404         
26405         for(var i = 0, finger; finger = touches[i]; i++){
26406             coords.push(finger.pageX, finger.pageY);
26407         }
26408         
26409         var x = Math.pow(coords[0] - coords[2], 2);
26410         var y = Math.pow(coords[1] - coords[3], 2);
26411         
26412         this.endDistance = Math.sqrt(x + y);
26413         
26414         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26415         
26416         if(!this.zoomable()){
26417             this.scale = this.startScale;
26418             return;
26419         }
26420         
26421         this.draw();
26422         
26423     },
26424     
26425     onTouchEnd : function(e)
26426     {
26427         this.pinching = false;
26428         this.dragable = false;
26429         
26430     },
26431     
26432     process : function(file, crop)
26433     {
26434         if(this.loadMask){
26435             this.maskEl.mask(this.loadingText);
26436         }
26437         
26438         this.xhr = new XMLHttpRequest();
26439         
26440         file.xhr = this.xhr;
26441
26442         this.xhr.open(this.method, this.url, true);
26443         
26444         var headers = {
26445             "Accept": "application/json",
26446             "Cache-Control": "no-cache",
26447             "X-Requested-With": "XMLHttpRequest"
26448         };
26449         
26450         for (var headerName in headers) {
26451             var headerValue = headers[headerName];
26452             if (headerValue) {
26453                 this.xhr.setRequestHeader(headerName, headerValue);
26454             }
26455         }
26456         
26457         var _this = this;
26458         
26459         this.xhr.onload = function()
26460         {
26461             _this.xhrOnLoad(_this.xhr);
26462         }
26463         
26464         this.xhr.onerror = function()
26465         {
26466             _this.xhrOnError(_this.xhr);
26467         }
26468         
26469         var formData = new FormData();
26470
26471         formData.append('returnHTML', 'NO');
26472         
26473         if(crop){
26474             formData.append('crop', crop);
26475         }
26476         
26477         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26478             formData.append(this.paramName, file, file.name);
26479         }
26480         
26481         if(typeof(file.filename) != 'undefined'){
26482             formData.append('filename', file.filename);
26483         }
26484         
26485         if(typeof(file.mimetype) != 'undefined'){
26486             formData.append('mimetype', file.mimetype);
26487         }
26488         
26489         if(this.fireEvent('arrange', this, formData) != false){
26490             this.xhr.send(formData);
26491         };
26492     },
26493     
26494     xhrOnLoad : function(xhr)
26495     {
26496         if(this.loadMask){
26497             this.maskEl.unmask();
26498         }
26499         
26500         if (xhr.readyState !== 4) {
26501             this.fireEvent('exception', this, xhr);
26502             return;
26503         }
26504
26505         var response = Roo.decode(xhr.responseText);
26506         
26507         if(!response.success){
26508             this.fireEvent('exception', this, xhr);
26509             return;
26510         }
26511         
26512         var response = Roo.decode(xhr.responseText);
26513         
26514         this.fireEvent('upload', this, response);
26515         
26516     },
26517     
26518     xhrOnError : function()
26519     {
26520         if(this.loadMask){
26521             this.maskEl.unmask();
26522         }
26523         
26524         Roo.log('xhr on error');
26525         
26526         var response = Roo.decode(xhr.responseText);
26527           
26528         Roo.log(response);
26529         
26530     },
26531     
26532     prepare : function(file)
26533     {   
26534         if(this.loadMask){
26535             this.maskEl.mask(this.loadingText);
26536         }
26537         
26538         this.file = false;
26539         this.exif = {};
26540         
26541         if(typeof(file) === 'string'){
26542             this.loadCanvas(file);
26543             return;
26544         }
26545         
26546         if(!file || !this.urlAPI){
26547             return;
26548         }
26549         
26550         this.file = file;
26551         this.cropType = file.type;
26552         
26553         var _this = this;
26554         
26555         if(this.fireEvent('prepare', this, this.file) != false){
26556             
26557             var reader = new FileReader();
26558             
26559             reader.onload = function (e) {
26560                 if (e.target.error) {
26561                     Roo.log(e.target.error);
26562                     return;
26563                 }
26564                 
26565                 var buffer = e.target.result,
26566                     dataView = new DataView(buffer),
26567                     offset = 2,
26568                     maxOffset = dataView.byteLength - 4,
26569                     markerBytes,
26570                     markerLength;
26571                 
26572                 if (dataView.getUint16(0) === 0xffd8) {
26573                     while (offset < maxOffset) {
26574                         markerBytes = dataView.getUint16(offset);
26575                         
26576                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26577                             markerLength = dataView.getUint16(offset + 2) + 2;
26578                             if (offset + markerLength > dataView.byteLength) {
26579                                 Roo.log('Invalid meta data: Invalid segment size.');
26580                                 break;
26581                             }
26582                             
26583                             if(markerBytes == 0xffe1){
26584                                 _this.parseExifData(
26585                                     dataView,
26586                                     offset,
26587                                     markerLength
26588                                 );
26589                             }
26590                             
26591                             offset += markerLength;
26592                             
26593                             continue;
26594                         }
26595                         
26596                         break;
26597                     }
26598                     
26599                 }
26600                 
26601                 var url = _this.urlAPI.createObjectURL(_this.file);
26602                 
26603                 _this.loadCanvas(url);
26604                 
26605                 return;
26606             }
26607             
26608             reader.readAsArrayBuffer(this.file);
26609             
26610         }
26611         
26612     },
26613     
26614     parseExifData : function(dataView, offset, length)
26615     {
26616         var tiffOffset = offset + 10,
26617             littleEndian,
26618             dirOffset;
26619     
26620         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26621             // No Exif data, might be XMP data instead
26622             return;
26623         }
26624         
26625         // Check for the ASCII code for "Exif" (0x45786966):
26626         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26627             // No Exif data, might be XMP data instead
26628             return;
26629         }
26630         if (tiffOffset + 8 > dataView.byteLength) {
26631             Roo.log('Invalid Exif data: Invalid segment size.');
26632             return;
26633         }
26634         // Check for the two null bytes:
26635         if (dataView.getUint16(offset + 8) !== 0x0000) {
26636             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26637             return;
26638         }
26639         // Check the byte alignment:
26640         switch (dataView.getUint16(tiffOffset)) {
26641         case 0x4949:
26642             littleEndian = true;
26643             break;
26644         case 0x4D4D:
26645             littleEndian = false;
26646             break;
26647         default:
26648             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26649             return;
26650         }
26651         // Check for the TIFF tag marker (0x002A):
26652         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26653             Roo.log('Invalid Exif data: Missing TIFF marker.');
26654             return;
26655         }
26656         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26657         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26658         
26659         this.parseExifTags(
26660             dataView,
26661             tiffOffset,
26662             tiffOffset + dirOffset,
26663             littleEndian
26664         );
26665     },
26666     
26667     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26668     {
26669         var tagsNumber,
26670             dirEndOffset,
26671             i;
26672         if (dirOffset + 6 > dataView.byteLength) {
26673             Roo.log('Invalid Exif data: Invalid directory offset.');
26674             return;
26675         }
26676         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26677         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26678         if (dirEndOffset + 4 > dataView.byteLength) {
26679             Roo.log('Invalid Exif data: Invalid directory size.');
26680             return;
26681         }
26682         for (i = 0; i < tagsNumber; i += 1) {
26683             this.parseExifTag(
26684                 dataView,
26685                 tiffOffset,
26686                 dirOffset + 2 + 12 * i, // tag offset
26687                 littleEndian
26688             );
26689         }
26690         // Return the offset to the next directory:
26691         return dataView.getUint32(dirEndOffset, littleEndian);
26692     },
26693     
26694     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26695     {
26696         var tag = dataView.getUint16(offset, littleEndian);
26697         
26698         this.exif[tag] = this.getExifValue(
26699             dataView,
26700             tiffOffset,
26701             offset,
26702             dataView.getUint16(offset + 2, littleEndian), // tag type
26703             dataView.getUint32(offset + 4, littleEndian), // tag length
26704             littleEndian
26705         );
26706     },
26707     
26708     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26709     {
26710         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26711             tagSize,
26712             dataOffset,
26713             values,
26714             i,
26715             str,
26716             c;
26717     
26718         if (!tagType) {
26719             Roo.log('Invalid Exif data: Invalid tag type.');
26720             return;
26721         }
26722         
26723         tagSize = tagType.size * length;
26724         // Determine if the value is contained in the dataOffset bytes,
26725         // or if the value at the dataOffset is a pointer to the actual data:
26726         dataOffset = tagSize > 4 ?
26727                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26728         if (dataOffset + tagSize > dataView.byteLength) {
26729             Roo.log('Invalid Exif data: Invalid data offset.');
26730             return;
26731         }
26732         if (length === 1) {
26733             return tagType.getValue(dataView, dataOffset, littleEndian);
26734         }
26735         values = [];
26736         for (i = 0; i < length; i += 1) {
26737             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26738         }
26739         
26740         if (tagType.ascii) {
26741             str = '';
26742             // Concatenate the chars:
26743             for (i = 0; i < values.length; i += 1) {
26744                 c = values[i];
26745                 // Ignore the terminating NULL byte(s):
26746                 if (c === '\u0000') {
26747                     break;
26748                 }
26749                 str += c;
26750             }
26751             return str;
26752         }
26753         return values;
26754     }
26755     
26756 });
26757
26758 Roo.apply(Roo.bootstrap.UploadCropbox, {
26759     tags : {
26760         'Orientation': 0x0112
26761     },
26762     
26763     Orientation: {
26764             1: 0, //'top-left',
26765 //            2: 'top-right',
26766             3: 180, //'bottom-right',
26767 //            4: 'bottom-left',
26768 //            5: 'left-top',
26769             6: 90, //'right-top',
26770 //            7: 'right-bottom',
26771             8: 270 //'left-bottom'
26772     },
26773     
26774     exifTagTypes : {
26775         // byte, 8-bit unsigned int:
26776         1: {
26777             getValue: function (dataView, dataOffset) {
26778                 return dataView.getUint8(dataOffset);
26779             },
26780             size: 1
26781         },
26782         // ascii, 8-bit byte:
26783         2: {
26784             getValue: function (dataView, dataOffset) {
26785                 return String.fromCharCode(dataView.getUint8(dataOffset));
26786             },
26787             size: 1,
26788             ascii: true
26789         },
26790         // short, 16 bit int:
26791         3: {
26792             getValue: function (dataView, dataOffset, littleEndian) {
26793                 return dataView.getUint16(dataOffset, littleEndian);
26794             },
26795             size: 2
26796         },
26797         // long, 32 bit int:
26798         4: {
26799             getValue: function (dataView, dataOffset, littleEndian) {
26800                 return dataView.getUint32(dataOffset, littleEndian);
26801             },
26802             size: 4
26803         },
26804         // rational = two long values, first is numerator, second is denominator:
26805         5: {
26806             getValue: function (dataView, dataOffset, littleEndian) {
26807                 return dataView.getUint32(dataOffset, littleEndian) /
26808                     dataView.getUint32(dataOffset + 4, littleEndian);
26809             },
26810             size: 8
26811         },
26812         // slong, 32 bit signed int:
26813         9: {
26814             getValue: function (dataView, dataOffset, littleEndian) {
26815                 return dataView.getInt32(dataOffset, littleEndian);
26816             },
26817             size: 4
26818         },
26819         // srational, two slongs, first is numerator, second is denominator:
26820         10: {
26821             getValue: function (dataView, dataOffset, littleEndian) {
26822                 return dataView.getInt32(dataOffset, littleEndian) /
26823                     dataView.getInt32(dataOffset + 4, littleEndian);
26824             },
26825             size: 8
26826         }
26827     },
26828     
26829     footer : {
26830         STANDARD : [
26831             {
26832                 tag : 'div',
26833                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26834                 action : 'rotate-left',
26835                 cn : [
26836                     {
26837                         tag : 'button',
26838                         cls : 'btn btn-default',
26839                         html : '<i class="fa fa-undo"></i>'
26840                     }
26841                 ]
26842             },
26843             {
26844                 tag : 'div',
26845                 cls : 'btn-group roo-upload-cropbox-picture',
26846                 action : 'picture',
26847                 cn : [
26848                     {
26849                         tag : 'button',
26850                         cls : 'btn btn-default',
26851                         html : '<i class="fa fa-picture-o"></i>'
26852                     }
26853                 ]
26854             },
26855             {
26856                 tag : 'div',
26857                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26858                 action : 'rotate-right',
26859                 cn : [
26860                     {
26861                         tag : 'button',
26862                         cls : 'btn btn-default',
26863                         html : '<i class="fa fa-repeat"></i>'
26864                     }
26865                 ]
26866             }
26867         ],
26868         DOCUMENT : [
26869             {
26870                 tag : 'div',
26871                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26872                 action : 'rotate-left',
26873                 cn : [
26874                     {
26875                         tag : 'button',
26876                         cls : 'btn btn-default',
26877                         html : '<i class="fa fa-undo"></i>'
26878                     }
26879                 ]
26880             },
26881             {
26882                 tag : 'div',
26883                 cls : 'btn-group roo-upload-cropbox-download',
26884                 action : 'download',
26885                 cn : [
26886                     {
26887                         tag : 'button',
26888                         cls : 'btn btn-default',
26889                         html : '<i class="fa fa-download"></i>'
26890                     }
26891                 ]
26892             },
26893             {
26894                 tag : 'div',
26895                 cls : 'btn-group roo-upload-cropbox-crop',
26896                 action : 'crop',
26897                 cn : [
26898                     {
26899                         tag : 'button',
26900                         cls : 'btn btn-default',
26901                         html : '<i class="fa fa-crop"></i>'
26902                     }
26903                 ]
26904             },
26905             {
26906                 tag : 'div',
26907                 cls : 'btn-group roo-upload-cropbox-trash',
26908                 action : 'trash',
26909                 cn : [
26910                     {
26911                         tag : 'button',
26912                         cls : 'btn btn-default',
26913                         html : '<i class="fa fa-trash"></i>'
26914                     }
26915                 ]
26916             },
26917             {
26918                 tag : 'div',
26919                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26920                 action : 'rotate-right',
26921                 cn : [
26922                     {
26923                         tag : 'button',
26924                         cls : 'btn btn-default',
26925                         html : '<i class="fa fa-repeat"></i>'
26926                     }
26927                 ]
26928             }
26929         ],
26930         ROTATOR : [
26931             {
26932                 tag : 'div',
26933                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26934                 action : 'rotate-left',
26935                 cn : [
26936                     {
26937                         tag : 'button',
26938                         cls : 'btn btn-default',
26939                         html : '<i class="fa fa-undo"></i>'
26940                     }
26941                 ]
26942             },
26943             {
26944                 tag : 'div',
26945                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26946                 action : 'rotate-right',
26947                 cn : [
26948                     {
26949                         tag : 'button',
26950                         cls : 'btn btn-default',
26951                         html : '<i class="fa fa-repeat"></i>'
26952                     }
26953                 ]
26954             }
26955         ]
26956     }
26957 });
26958
26959 /*
26960 * Licence: LGPL
26961 */
26962
26963 /**
26964  * @class Roo.bootstrap.DocumentManager
26965  * @extends Roo.bootstrap.Component
26966  * Bootstrap DocumentManager class
26967  * @cfg {String} paramName default 'imageUpload'
26968  * @cfg {String} method default POST
26969  * @cfg {String} url action url
26970  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26971  * @cfg {Boolean} multiple multiple upload default true
26972  * @cfg {Number} thumbSize default 300
26973  * @cfg {String} fieldLabel
26974  * @cfg {Number} labelWidth default 4
26975  * @cfg {String} labelAlign (left|top) default left
26976  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26977  * 
26978  * @constructor
26979  * Create a new DocumentManager
26980  * @param {Object} config The config object
26981  */
26982
26983 Roo.bootstrap.DocumentManager = function(config){
26984     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26985     
26986     this.addEvents({
26987         /**
26988          * @event initial
26989          * Fire when initial the DocumentManager
26990          * @param {Roo.bootstrap.DocumentManager} this
26991          */
26992         "initial" : true,
26993         /**
26994          * @event inspect
26995          * inspect selected file
26996          * @param {Roo.bootstrap.DocumentManager} this
26997          * @param {File} file
26998          */
26999         "inspect" : true,
27000         /**
27001          * @event exception
27002          * Fire when xhr load exception
27003          * @param {Roo.bootstrap.DocumentManager} this
27004          * @param {XMLHttpRequest} xhr
27005          */
27006         "exception" : true,
27007         /**
27008          * @event prepare
27009          * prepare the form data
27010          * @param {Roo.bootstrap.DocumentManager} this
27011          * @param {Object} formData
27012          */
27013         "prepare" : true,
27014         /**
27015          * @event remove
27016          * Fire when remove the file
27017          * @param {Roo.bootstrap.DocumentManager} this
27018          * @param {Object} file
27019          */
27020         "remove" : true,
27021         /**
27022          * @event refresh
27023          * Fire after refresh the file
27024          * @param {Roo.bootstrap.DocumentManager} this
27025          */
27026         "refresh" : true,
27027         /**
27028          * @event click
27029          * Fire after click the image
27030          * @param {Roo.bootstrap.DocumentManager} this
27031          * @param {Object} file
27032          */
27033         "click" : true,
27034         /**
27035          * @event edit
27036          * Fire when upload a image and editable set to true
27037          * @param {Roo.bootstrap.DocumentManager} this
27038          * @param {Object} file
27039          */
27040         "edit" : true,
27041         /**
27042          * @event beforeselectfile
27043          * Fire before select file
27044          * @param {Roo.bootstrap.DocumentManager} this
27045          */
27046         "beforeselectfile" : true,
27047         /**
27048          * @event process
27049          * Fire before process file
27050          * @param {Roo.bootstrap.DocumentManager} this
27051          * @param {Object} file
27052          */
27053         "process" : true
27054         
27055     });
27056 };
27057
27058 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27059     
27060     boxes : 0,
27061     inputName : '',
27062     thumbSize : 300,
27063     multiple : true,
27064     files : [],
27065     method : 'POST',
27066     url : '',
27067     paramName : 'imageUpload',
27068     fieldLabel : '',
27069     labelWidth : 4,
27070     labelAlign : 'left',
27071     editable : true,
27072     delegates : [],
27073     
27074     
27075     xhr : false, 
27076     
27077     getAutoCreate : function()
27078     {   
27079         var managerWidget = {
27080             tag : 'div',
27081             cls : 'roo-document-manager',
27082             cn : [
27083                 {
27084                     tag : 'input',
27085                     cls : 'roo-document-manager-selector',
27086                     type : 'file'
27087                 },
27088                 {
27089                     tag : 'div',
27090                     cls : 'roo-document-manager-uploader',
27091                     cn : [
27092                         {
27093                             tag : 'div',
27094                             cls : 'roo-document-manager-upload-btn',
27095                             html : '<i class="fa fa-plus"></i>'
27096                         }
27097                     ]
27098                     
27099                 }
27100             ]
27101         };
27102         
27103         var content = [
27104             {
27105                 tag : 'div',
27106                 cls : 'column col-md-12',
27107                 cn : managerWidget
27108             }
27109         ];
27110         
27111         if(this.fieldLabel.length){
27112             
27113             content = [
27114                 {
27115                     tag : 'div',
27116                     cls : 'column col-md-12',
27117                     html : this.fieldLabel
27118                 },
27119                 {
27120                     tag : 'div',
27121                     cls : 'column col-md-12',
27122                     cn : managerWidget
27123                 }
27124             ];
27125
27126             if(this.labelAlign == 'left'){
27127                 content = [
27128                     {
27129                         tag : 'div',
27130                         cls : 'column col-md-' + this.labelWidth,
27131                         html : this.fieldLabel
27132                     },
27133                     {
27134                         tag : 'div',
27135                         cls : 'column col-md-' + (12 - this.labelWidth),
27136                         cn : managerWidget
27137                     }
27138                 ];
27139                 
27140             }
27141         }
27142         
27143         var cfg = {
27144             tag : 'div',
27145             cls : 'row clearfix',
27146             cn : content
27147         };
27148         
27149         return cfg;
27150         
27151     },
27152     
27153     initEvents : function()
27154     {
27155         this.managerEl = this.el.select('.roo-document-manager', true).first();
27156         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27157         
27158         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27159         this.selectorEl.hide();
27160         
27161         if(this.multiple){
27162             this.selectorEl.attr('multiple', 'multiple');
27163         }
27164         
27165         this.selectorEl.on('change', this.onFileSelected, this);
27166         
27167         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27168         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27169         
27170         this.uploader.on('click', this.onUploaderClick, this);
27171         
27172         this.renderProgressDialog();
27173         
27174         var _this = this;
27175         
27176         window.addEventListener("resize", function() { _this.refresh(); } );
27177         
27178         this.fireEvent('initial', this);
27179     },
27180     
27181     renderProgressDialog : function()
27182     {
27183         var _this = this;
27184         
27185         this.progressDialog = new Roo.bootstrap.Modal({
27186             cls : 'roo-document-manager-progress-dialog',
27187             allow_close : false,
27188             title : '',
27189             buttons : [
27190                 {
27191                     name  :'cancel',
27192                     weight : 'danger',
27193                     html : 'Cancel'
27194                 }
27195             ], 
27196             listeners : { 
27197                 btnclick : function() {
27198                     _this.uploadCancel();
27199                     this.hide();
27200                 }
27201             }
27202         });
27203          
27204         this.progressDialog.render(Roo.get(document.body));
27205          
27206         this.progress = new Roo.bootstrap.Progress({
27207             cls : 'roo-document-manager-progress',
27208             active : true,
27209             striped : true
27210         });
27211         
27212         this.progress.render(this.progressDialog.getChildContainer());
27213         
27214         this.progressBar = new Roo.bootstrap.ProgressBar({
27215             cls : 'roo-document-manager-progress-bar',
27216             aria_valuenow : 0,
27217             aria_valuemin : 0,
27218             aria_valuemax : 12,
27219             panel : 'success'
27220         });
27221         
27222         this.progressBar.render(this.progress.getChildContainer());
27223     },
27224     
27225     onUploaderClick : function(e)
27226     {
27227         e.preventDefault();
27228      
27229         if(this.fireEvent('beforeselectfile', this) != false){
27230             this.selectorEl.dom.click();
27231         }
27232         
27233     },
27234     
27235     onFileSelected : function(e)
27236     {
27237         e.preventDefault();
27238         
27239         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27240             return;
27241         }
27242         
27243         Roo.each(this.selectorEl.dom.files, function(file){
27244             if(this.fireEvent('inspect', this, file) != false){
27245                 this.files.push(file);
27246             }
27247         }, this);
27248         
27249         this.queue();
27250         
27251     },
27252     
27253     queue : function()
27254     {
27255         this.selectorEl.dom.value = '';
27256         
27257         if(!this.files.length){
27258             return;
27259         }
27260         
27261         if(this.boxes > 0 && this.files.length > this.boxes){
27262             this.files = this.files.slice(0, this.boxes);
27263         }
27264         
27265         this.uploader.show();
27266         
27267         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27268             this.uploader.hide();
27269         }
27270         
27271         var _this = this;
27272         
27273         var files = [];
27274         
27275         var docs = [];
27276         
27277         Roo.each(this.files, function(file){
27278             
27279             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27280                 var f = this.renderPreview(file);
27281                 files.push(f);
27282                 return;
27283             }
27284             
27285             if(file.type.indexOf('image') != -1){
27286                 this.delegates.push(
27287                     (function(){
27288                         _this.process(file);
27289                     }).createDelegate(this)
27290                 );
27291         
27292                 return;
27293             }
27294             
27295             docs.push(
27296                 (function(){
27297                     _this.process(file);
27298                 }).createDelegate(this)
27299             );
27300             
27301         }, this);
27302         
27303         this.files = files;
27304         
27305         this.delegates = this.delegates.concat(docs);
27306         
27307         if(!this.delegates.length){
27308             this.refresh();
27309             return;
27310         }
27311         
27312         this.progressBar.aria_valuemax = this.delegates.length;
27313         
27314         this.arrange();
27315         
27316         return;
27317     },
27318     
27319     arrange : function()
27320     {
27321         if(!this.delegates.length){
27322             this.progressDialog.hide();
27323             this.refresh();
27324             return;
27325         }
27326         
27327         var delegate = this.delegates.shift();
27328         
27329         this.progressDialog.show();
27330         
27331         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27332         
27333         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27334         
27335         delegate();
27336     },
27337     
27338     refresh : function()
27339     {
27340         this.uploader.show();
27341         
27342         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27343             this.uploader.hide();
27344         }
27345         
27346         Roo.isTouch ? this.closable(false) : this.closable(true);
27347         
27348         this.fireEvent('refresh', this);
27349     },
27350     
27351     onRemove : function(e, el, o)
27352     {
27353         e.preventDefault();
27354         
27355         this.fireEvent('remove', this, o);
27356         
27357     },
27358     
27359     remove : function(o)
27360     {
27361         var files = [];
27362         
27363         Roo.each(this.files, function(file){
27364             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27365                 files.push(file);
27366                 return;
27367             }
27368
27369             o.target.remove();
27370
27371         }, this);
27372         
27373         this.files = files;
27374         
27375         this.refresh();
27376     },
27377     
27378     clear : function()
27379     {
27380         Roo.each(this.files, function(file){
27381             if(!file.target){
27382                 return;
27383             }
27384             
27385             file.target.remove();
27386
27387         }, this);
27388         
27389         this.files = [];
27390         
27391         this.refresh();
27392     },
27393     
27394     onClick : function(e, el, o)
27395     {
27396         e.preventDefault();
27397         
27398         this.fireEvent('click', this, o);
27399         
27400     },
27401     
27402     closable : function(closable)
27403     {
27404         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27405             
27406             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27407             
27408             if(closable){
27409                 el.show();
27410                 return;
27411             }
27412             
27413             el.hide();
27414             
27415         }, this);
27416     },
27417     
27418     xhrOnLoad : function(xhr)
27419     {
27420         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27421             el.remove();
27422         }, this);
27423         
27424         if (xhr.readyState !== 4) {
27425             this.arrange();
27426             this.fireEvent('exception', this, xhr);
27427             return;
27428         }
27429
27430         var response = Roo.decode(xhr.responseText);
27431         
27432         if(!response.success){
27433             this.arrange();
27434             this.fireEvent('exception', this, xhr);
27435             return;
27436         }
27437         
27438         var file = this.renderPreview(response.data);
27439         
27440         this.files.push(file);
27441         
27442         this.arrange();
27443         
27444     },
27445     
27446     xhrOnError : function(xhr)
27447     {
27448         Roo.log('xhr on error');
27449         
27450         var response = Roo.decode(xhr.responseText);
27451           
27452         Roo.log(response);
27453         
27454         this.arrange();
27455     },
27456     
27457     process : function(file)
27458     {
27459         if(this.fireEvent('process', this, file) !== false){
27460             if(this.editable && file.type.indexOf('image') != -1){
27461                 this.fireEvent('edit', this, file);
27462                 return;
27463             }
27464
27465             this.uploadStart(file, false);
27466
27467             return;
27468         }
27469         
27470     },
27471     
27472     uploadStart : function(file, crop)
27473     {
27474         this.xhr = new XMLHttpRequest();
27475         
27476         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27477             this.arrange();
27478             return;
27479         }
27480         
27481         file.xhr = this.xhr;
27482             
27483         this.managerEl.createChild({
27484             tag : 'div',
27485             cls : 'roo-document-manager-loading',
27486             cn : [
27487                 {
27488                     tag : 'div',
27489                     tooltip : file.name,
27490                     cls : 'roo-document-manager-thumb',
27491                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27492                 }
27493             ]
27494
27495         });
27496
27497         this.xhr.open(this.method, this.url, true);
27498         
27499         var headers = {
27500             "Accept": "application/json",
27501             "Cache-Control": "no-cache",
27502             "X-Requested-With": "XMLHttpRequest"
27503         };
27504         
27505         for (var headerName in headers) {
27506             var headerValue = headers[headerName];
27507             if (headerValue) {
27508                 this.xhr.setRequestHeader(headerName, headerValue);
27509             }
27510         }
27511         
27512         var _this = this;
27513         
27514         this.xhr.onload = function()
27515         {
27516             _this.xhrOnLoad(_this.xhr);
27517         }
27518         
27519         this.xhr.onerror = function()
27520         {
27521             _this.xhrOnError(_this.xhr);
27522         }
27523         
27524         var formData = new FormData();
27525
27526         formData.append('returnHTML', 'NO');
27527         
27528         if(crop){
27529             formData.append('crop', crop);
27530         }
27531         
27532         formData.append(this.paramName, file, file.name);
27533         
27534         if(this.fireEvent('prepare', this, formData) != false){
27535             this.xhr.send(formData);
27536         };
27537     },
27538     
27539     uploadCancel : function()
27540     {
27541         if (this.xhr) {
27542             this.xhr.abort();
27543         }
27544         
27545         
27546         this.delegates = [];
27547         
27548         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27549             el.remove();
27550         }, this);
27551         
27552         this.arrange();
27553     },
27554     
27555     renderPreview : function(file)
27556     {
27557         if(typeof(file.target) != 'undefined' && file.target){
27558             return file;
27559         }
27560         
27561         var previewEl = this.managerEl.createChild({
27562             tag : 'div',
27563             cls : 'roo-document-manager-preview',
27564             cn : [
27565                 {
27566                     tag : 'div',
27567                     tooltip : file.filename,
27568                     cls : 'roo-document-manager-thumb',
27569                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27570                 },
27571                 {
27572                     tag : 'button',
27573                     cls : 'close',
27574                     html : '<i class="fa fa-times-circle"></i>'
27575                 }
27576             ]
27577         });
27578
27579         var close = previewEl.select('button.close', true).first();
27580
27581         close.on('click', this.onRemove, this, file);
27582
27583         file.target = previewEl;
27584
27585         var image = previewEl.select('img', true).first();
27586         
27587         var _this = this;
27588         
27589         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27590         
27591         image.on('click', this.onClick, this, file);
27592         
27593         return file;
27594         
27595     },
27596     
27597     onPreviewLoad : function(file, image)
27598     {
27599         if(typeof(file.target) == 'undefined' || !file.target){
27600             return;
27601         }
27602         
27603         var width = image.dom.naturalWidth || image.dom.width;
27604         var height = image.dom.naturalHeight || image.dom.height;
27605         
27606         if(width > height){
27607             file.target.addClass('wide');
27608             return;
27609         }
27610         
27611         file.target.addClass('tall');
27612         return;
27613         
27614     },
27615     
27616     uploadFromSource : function(file, crop)
27617     {
27618         this.xhr = new XMLHttpRequest();
27619         
27620         this.managerEl.createChild({
27621             tag : 'div',
27622             cls : 'roo-document-manager-loading',
27623             cn : [
27624                 {
27625                     tag : 'div',
27626                     tooltip : file.name,
27627                     cls : 'roo-document-manager-thumb',
27628                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27629                 }
27630             ]
27631
27632         });
27633
27634         this.xhr.open(this.method, this.url, true);
27635         
27636         var headers = {
27637             "Accept": "application/json",
27638             "Cache-Control": "no-cache",
27639             "X-Requested-With": "XMLHttpRequest"
27640         };
27641         
27642         for (var headerName in headers) {
27643             var headerValue = headers[headerName];
27644             if (headerValue) {
27645                 this.xhr.setRequestHeader(headerName, headerValue);
27646             }
27647         }
27648         
27649         var _this = this;
27650         
27651         this.xhr.onload = function()
27652         {
27653             _this.xhrOnLoad(_this.xhr);
27654         }
27655         
27656         this.xhr.onerror = function()
27657         {
27658             _this.xhrOnError(_this.xhr);
27659         }
27660         
27661         var formData = new FormData();
27662
27663         formData.append('returnHTML', 'NO');
27664         
27665         formData.append('crop', crop);
27666         
27667         if(typeof(file.filename) != 'undefined'){
27668             formData.append('filename', file.filename);
27669         }
27670         
27671         if(typeof(file.mimetype) != 'undefined'){
27672             formData.append('mimetype', file.mimetype);
27673         }
27674         
27675         if(this.fireEvent('prepare', this, formData) != false){
27676             this.xhr.send(formData);
27677         };
27678     }
27679 });
27680
27681 /*
27682 * Licence: LGPL
27683 */
27684
27685 /**
27686  * @class Roo.bootstrap.DocumentViewer
27687  * @extends Roo.bootstrap.Component
27688  * Bootstrap DocumentViewer class
27689  * 
27690  * @constructor
27691  * Create a new DocumentViewer
27692  * @param {Object} config The config object
27693  */
27694
27695 Roo.bootstrap.DocumentViewer = function(config){
27696     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27697     
27698     this.addEvents({
27699         /**
27700          * @event initial
27701          * Fire after initEvent
27702          * @param {Roo.bootstrap.DocumentViewer} this
27703          */
27704         "initial" : true,
27705         /**
27706          * @event click
27707          * Fire after click
27708          * @param {Roo.bootstrap.DocumentViewer} this
27709          */
27710         "click" : true,
27711         /**
27712          * @event trash
27713          * Fire after trash button
27714          * @param {Roo.bootstrap.DocumentViewer} this
27715          */
27716         "trash" : true
27717         
27718     });
27719 };
27720
27721 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27722     
27723     getAutoCreate : function()
27724     {
27725         var cfg = {
27726             tag : 'div',
27727             cls : 'roo-document-viewer',
27728             cn : [
27729                 {
27730                     tag : 'div',
27731                     cls : 'roo-document-viewer-body',
27732                     cn : [
27733                         {
27734                             tag : 'div',
27735                             cls : 'roo-document-viewer-thumb',
27736                             cn : [
27737                                 {
27738                                     tag : 'img',
27739                                     cls : 'roo-document-viewer-image'
27740                                 }
27741                             ]
27742                         }
27743                     ]
27744                 },
27745                 {
27746                     tag : 'div',
27747                     cls : 'roo-document-viewer-footer',
27748                     cn : {
27749                         tag : 'div',
27750                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27751                         cn : [
27752                             {
27753                                 tag : 'div',
27754                                 cls : 'btn-group',
27755                                 cn : [
27756                                     {
27757                                         tag : 'button',
27758                                         cls : 'btn btn-default roo-document-viewer-trash',
27759                                         html : '<i class="fa fa-trash"></i>'
27760                                     }
27761                                 ]
27762                             }
27763                         ]
27764                     }
27765                 }
27766             ]
27767         };
27768         
27769         return cfg;
27770     },
27771     
27772     initEvents : function()
27773     {
27774         
27775         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27776         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27777         
27778         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27779         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27780         
27781         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27782         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27783         
27784         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27785         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27786         
27787         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27788         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27789         
27790         this.bodyEl.on('click', this.onClick, this);
27791         
27792         this.trashBtn.on('click', this.onTrash, this);
27793         
27794     },
27795     
27796     initial : function()
27797     {
27798 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27799         
27800         
27801         this.fireEvent('initial', this);
27802         
27803     },
27804     
27805     onClick : function(e)
27806     {
27807         e.preventDefault();
27808         
27809         this.fireEvent('click', this);
27810     },
27811     
27812     onTrash : function(e)
27813     {
27814         e.preventDefault();
27815         
27816         this.fireEvent('trash', this);
27817     }
27818     
27819 });
27820 /*
27821  * - LGPL
27822  *
27823  * nav progress bar
27824  * 
27825  */
27826
27827 /**
27828  * @class Roo.bootstrap.NavProgressBar
27829  * @extends Roo.bootstrap.Component
27830  * Bootstrap NavProgressBar class
27831  * 
27832  * @constructor
27833  * Create a new nav progress bar
27834  * @param {Object} config The config object
27835  */
27836
27837 Roo.bootstrap.NavProgressBar = function(config){
27838     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27839
27840     this.bullets = this.bullets || [];
27841    
27842 //    Roo.bootstrap.NavProgressBar.register(this);
27843      this.addEvents({
27844         /**
27845              * @event changed
27846              * Fires when the active item changes
27847              * @param {Roo.bootstrap.NavProgressBar} this
27848              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27849              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27850          */
27851         'changed': true
27852      });
27853     
27854 };
27855
27856 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27857     
27858     bullets : [],
27859     barItems : [],
27860     
27861     getAutoCreate : function()
27862     {
27863         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27864         
27865         cfg = {
27866             tag : 'div',
27867             cls : 'roo-navigation-bar-group',
27868             cn : [
27869                 {
27870                     tag : 'div',
27871                     cls : 'roo-navigation-top-bar'
27872                 },
27873                 {
27874                     tag : 'div',
27875                     cls : 'roo-navigation-bullets-bar',
27876                     cn : [
27877                         {
27878                             tag : 'ul',
27879                             cls : 'roo-navigation-bar'
27880                         }
27881                     ]
27882                 },
27883                 
27884                 {
27885                     tag : 'div',
27886                     cls : 'roo-navigation-bottom-bar'
27887                 }
27888             ]
27889             
27890         };
27891         
27892         return cfg;
27893         
27894     },
27895     
27896     initEvents: function() 
27897     {
27898         
27899     },
27900     
27901     onRender : function(ct, position) 
27902     {
27903         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27904         
27905         if(this.bullets.length){
27906             Roo.each(this.bullets, function(b){
27907                this.addItem(b);
27908             }, this);
27909         }
27910         
27911         this.format();
27912         
27913     },
27914     
27915     addItem : function(cfg)
27916     {
27917         var item = new Roo.bootstrap.NavProgressItem(cfg);
27918         
27919         item.parentId = this.id;
27920         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27921         
27922         if(cfg.html){
27923             var top = new Roo.bootstrap.Element({
27924                 tag : 'div',
27925                 cls : 'roo-navigation-bar-text'
27926             });
27927             
27928             var bottom = new Roo.bootstrap.Element({
27929                 tag : 'div',
27930                 cls : 'roo-navigation-bar-text'
27931             });
27932             
27933             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27934             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27935             
27936             var topText = new Roo.bootstrap.Element({
27937                 tag : 'span',
27938                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27939             });
27940             
27941             var bottomText = new Roo.bootstrap.Element({
27942                 tag : 'span',
27943                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27944             });
27945             
27946             topText.onRender(top.el, null);
27947             bottomText.onRender(bottom.el, null);
27948             
27949             item.topEl = top;
27950             item.bottomEl = bottom;
27951         }
27952         
27953         this.barItems.push(item);
27954         
27955         return item;
27956     },
27957     
27958     getActive : function()
27959     {
27960         var active = false;
27961         
27962         Roo.each(this.barItems, function(v){
27963             
27964             if (!v.isActive()) {
27965                 return;
27966             }
27967             
27968             active = v;
27969             return false;
27970             
27971         });
27972         
27973         return active;
27974     },
27975     
27976     setActiveItem : function(item)
27977     {
27978         var prev = false;
27979         
27980         Roo.each(this.barItems, function(v){
27981             if (v.rid == item.rid) {
27982                 return ;
27983             }
27984             
27985             if (v.isActive()) {
27986                 v.setActive(false);
27987                 prev = v;
27988             }
27989         });
27990
27991         item.setActive(true);
27992         
27993         this.fireEvent('changed', this, item, prev);
27994     },
27995     
27996     getBarItem: function(rid)
27997     {
27998         var ret = false;
27999         
28000         Roo.each(this.barItems, function(e) {
28001             if (e.rid != rid) {
28002                 return;
28003             }
28004             
28005             ret =  e;
28006             return false;
28007         });
28008         
28009         return ret;
28010     },
28011     
28012     indexOfItem : function(item)
28013     {
28014         var index = false;
28015         
28016         Roo.each(this.barItems, function(v, i){
28017             
28018             if (v.rid != item.rid) {
28019                 return;
28020             }
28021             
28022             index = i;
28023             return false
28024         });
28025         
28026         return index;
28027     },
28028     
28029     setActiveNext : function()
28030     {
28031         var i = this.indexOfItem(this.getActive());
28032         
28033         if (i > this.barItems.length) {
28034             return;
28035         }
28036         
28037         this.setActiveItem(this.barItems[i+1]);
28038     },
28039     
28040     setActivePrev : function()
28041     {
28042         var i = this.indexOfItem(this.getActive());
28043         
28044         if (i  < 1) {
28045             return;
28046         }
28047         
28048         this.setActiveItem(this.barItems[i-1]);
28049     },
28050     
28051     format : function()
28052     {
28053         if(!this.barItems.length){
28054             return;
28055         }
28056      
28057         var width = 100 / this.barItems.length;
28058         
28059         Roo.each(this.barItems, function(i){
28060             i.el.setStyle('width', width + '%');
28061             i.topEl.el.setStyle('width', width + '%');
28062             i.bottomEl.el.setStyle('width', width + '%');
28063         }, this);
28064         
28065     }
28066     
28067 });
28068 /*
28069  * - LGPL
28070  *
28071  * Nav Progress Item
28072  * 
28073  */
28074
28075 /**
28076  * @class Roo.bootstrap.NavProgressItem
28077  * @extends Roo.bootstrap.Component
28078  * Bootstrap NavProgressItem class
28079  * @cfg {String} rid the reference id
28080  * @cfg {Boolean} active (true|false) Is item active default false
28081  * @cfg {Boolean} disabled (true|false) Is item active default false
28082  * @cfg {String} html
28083  * @cfg {String} position (top|bottom) text position default bottom
28084  * @cfg {String} icon show icon instead of number
28085  * 
28086  * @constructor
28087  * Create a new NavProgressItem
28088  * @param {Object} config The config object
28089  */
28090 Roo.bootstrap.NavProgressItem = function(config){
28091     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28092     this.addEvents({
28093         // raw events
28094         /**
28095          * @event click
28096          * The raw click event for the entire grid.
28097          * @param {Roo.bootstrap.NavProgressItem} this
28098          * @param {Roo.EventObject} e
28099          */
28100         "click" : true
28101     });
28102    
28103 };
28104
28105 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28106     
28107     rid : '',
28108     active : false,
28109     disabled : false,
28110     html : '',
28111     position : 'bottom',
28112     icon : false,
28113     
28114     getAutoCreate : function()
28115     {
28116         var iconCls = 'roo-navigation-bar-item-icon';
28117         
28118         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28119         
28120         var cfg = {
28121             tag: 'li',
28122             cls: 'roo-navigation-bar-item',
28123             cn : [
28124                 {
28125                     tag : 'i',
28126                     cls : iconCls
28127                 }
28128             ]
28129         };
28130         
28131         if(this.active){
28132             cfg.cls += ' active';
28133         }
28134         if(this.disabled){
28135             cfg.cls += ' disabled';
28136         }
28137         
28138         return cfg;
28139     },
28140     
28141     disable : function()
28142     {
28143         this.setDisabled(true);
28144     },
28145     
28146     enable : function()
28147     {
28148         this.setDisabled(false);
28149     },
28150     
28151     initEvents: function() 
28152     {
28153         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28154         
28155         this.iconEl.on('click', this.onClick, this);
28156     },
28157     
28158     onClick : function(e)
28159     {
28160         e.preventDefault();
28161         
28162         if(this.disabled){
28163             return;
28164         }
28165         
28166         if(this.fireEvent('click', this, e) === false){
28167             return;
28168         };
28169         
28170         this.parent().setActiveItem(this);
28171     },
28172     
28173     isActive: function () 
28174     {
28175         return this.active;
28176     },
28177     
28178     setActive : function(state)
28179     {
28180         if(this.active == state){
28181             return;
28182         }
28183         
28184         this.active = state;
28185         
28186         if (state) {
28187             this.el.addClass('active');
28188             return;
28189         }
28190         
28191         this.el.removeClass('active');
28192         
28193         return;
28194     },
28195     
28196     setDisabled : function(state)
28197     {
28198         if(this.disabled == state){
28199             return;
28200         }
28201         
28202         this.disabled = state;
28203         
28204         if (state) {
28205             this.el.addClass('disabled');
28206             return;
28207         }
28208         
28209         this.el.removeClass('disabled');
28210     },
28211     
28212     tooltipEl : function()
28213     {
28214         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28215     }
28216 });
28217  
28218
28219  /*
28220  * - LGPL
28221  *
28222  * FieldLabel
28223  * 
28224  */
28225
28226 /**
28227  * @class Roo.bootstrap.FieldLabel
28228  * @extends Roo.bootstrap.Component
28229  * Bootstrap FieldLabel class
28230  * @cfg {String} html contents of the element
28231  * @cfg {String} tag tag of the element default label
28232  * @cfg {String} cls class of the element
28233  * @cfg {String} target label target 
28234  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28235  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28236  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28237  * @cfg {String} iconTooltip default "This field is required"
28238  * 
28239  * @constructor
28240  * Create a new FieldLabel
28241  * @param {Object} config The config object
28242  */
28243
28244 Roo.bootstrap.FieldLabel = function(config){
28245     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28246     
28247     this.addEvents({
28248             /**
28249              * @event invalid
28250              * Fires after the field has been marked as invalid.
28251              * @param {Roo.form.FieldLabel} this
28252              * @param {String} msg The validation message
28253              */
28254             invalid : true,
28255             /**
28256              * @event valid
28257              * Fires after the field has been validated with no errors.
28258              * @param {Roo.form.FieldLabel} this
28259              */
28260             valid : true
28261         });
28262 };
28263
28264 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28265     
28266     tag: 'label',
28267     cls: '',
28268     html: '',
28269     target: '',
28270     allowBlank : true,
28271     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28272     validClass : 'text-success fa fa-lg fa-check',
28273     iconTooltip : 'This field is required',
28274     
28275     getAutoCreate : function(){
28276         
28277         var cfg = {
28278             tag : this.tag,
28279             cls : 'roo-bootstrap-field-label ' + this.cls,
28280             for : this.target,
28281             cn : [
28282                 {
28283                     tag : 'i',
28284                     cls : '',
28285                     tooltip : this.iconTooltip
28286                 },
28287                 {
28288                     tag : 'span',
28289                     html : this.html
28290                 }
28291             ] 
28292         };
28293         
28294         return cfg;
28295     },
28296     
28297     initEvents: function() 
28298     {
28299         Roo.bootstrap.Element.superclass.initEvents.call(this);
28300         
28301         this.iconEl = this.el.select('i', true).first();
28302         
28303         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28304         
28305         Roo.bootstrap.FieldLabel.register(this);
28306     },
28307     
28308     /**
28309      * Mark this field as valid
28310      */
28311     markValid : function()
28312     {
28313         this.iconEl.show();
28314         
28315         this.iconEl.removeClass(this.invalidClass);
28316         
28317         this.iconEl.addClass(this.validClass);
28318         
28319         this.fireEvent('valid', this);
28320     },
28321     
28322     /**
28323      * Mark this field as invalid
28324      * @param {String} msg The validation message
28325      */
28326     markInvalid : function(msg)
28327     {
28328         this.iconEl.show();
28329         
28330         this.iconEl.removeClass(this.validClass);
28331         
28332         this.iconEl.addClass(this.invalidClass);
28333         
28334         this.fireEvent('invalid', this, msg);
28335     }
28336     
28337    
28338 });
28339
28340 Roo.apply(Roo.bootstrap.FieldLabel, {
28341     
28342     groups: {},
28343     
28344      /**
28345     * register a FieldLabel Group
28346     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28347     */
28348     register : function(label)
28349     {
28350         if(this.groups.hasOwnProperty(label.target)){
28351             return;
28352         }
28353      
28354         this.groups[label.target] = label;
28355         
28356     },
28357     /**
28358     * fetch a FieldLabel Group based on the target
28359     * @param {string} target
28360     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28361     */
28362     get: function(target) {
28363         if (typeof(this.groups[target]) == 'undefined') {
28364             return false;
28365         }
28366         
28367         return this.groups[target] ;
28368     }
28369 });
28370
28371  
28372
28373  /*
28374  * - LGPL
28375  *
28376  * page DateSplitField.
28377  * 
28378  */
28379
28380
28381 /**
28382  * @class Roo.bootstrap.DateSplitField
28383  * @extends Roo.bootstrap.Component
28384  * Bootstrap DateSplitField class
28385  * @cfg {string} fieldLabel - the label associated
28386  * @cfg {Number} labelWidth set the width of label (0-12)
28387  * @cfg {String} labelAlign (top|left)
28388  * @cfg {Boolean} dayAllowBlank (true|false) default false
28389  * @cfg {Boolean} monthAllowBlank (true|false) default false
28390  * @cfg {Boolean} yearAllowBlank (true|false) default false
28391  * @cfg {string} dayPlaceholder 
28392  * @cfg {string} monthPlaceholder
28393  * @cfg {string} yearPlaceholder
28394  * @cfg {string} dayFormat default 'd'
28395  * @cfg {string} monthFormat default 'm'
28396  * @cfg {string} yearFormat default 'Y'
28397
28398  *     
28399  * @constructor
28400  * Create a new DateSplitField
28401  * @param {Object} config The config object
28402  */
28403
28404 Roo.bootstrap.DateSplitField = function(config){
28405     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28406     
28407     this.addEvents({
28408         // raw events
28409          /**
28410          * @event years
28411          * getting the data of years
28412          * @param {Roo.bootstrap.DateSplitField} this
28413          * @param {Object} years
28414          */
28415         "years" : true,
28416         /**
28417          * @event days
28418          * getting the data of days
28419          * @param {Roo.bootstrap.DateSplitField} this
28420          * @param {Object} days
28421          */
28422         "days" : true,
28423         /**
28424          * @event invalid
28425          * Fires after the field has been marked as invalid.
28426          * @param {Roo.form.Field} this
28427          * @param {String} msg The validation message
28428          */
28429         invalid : true,
28430        /**
28431          * @event valid
28432          * Fires after the field has been validated with no errors.
28433          * @param {Roo.form.Field} this
28434          */
28435         valid : true
28436     });
28437 };
28438
28439 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28440     
28441     fieldLabel : '',
28442     labelAlign : 'top',
28443     labelWidth : 3,
28444     dayAllowBlank : false,
28445     monthAllowBlank : false,
28446     yearAllowBlank : false,
28447     dayPlaceholder : '',
28448     monthPlaceholder : '',
28449     yearPlaceholder : '',
28450     dayFormat : 'd',
28451     monthFormat : 'm',
28452     yearFormat : 'Y',
28453     isFormField : true,
28454     
28455     getAutoCreate : function()
28456     {
28457         var cfg = {
28458             tag : 'div',
28459             cls : 'row roo-date-split-field-group',
28460             cn : [
28461                 {
28462                     tag : 'input',
28463                     type : 'hidden',
28464                     cls : 'form-hidden-field roo-date-split-field-group-value',
28465                     name : this.name
28466                 }
28467             ]
28468         };
28469         
28470         if(this.fieldLabel){
28471             cfg.cn.push({
28472                 tag : 'div',
28473                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28474                 cn : [
28475                     {
28476                         tag : 'label',
28477                         html : this.fieldLabel
28478                     }
28479                 ]
28480             });
28481         }
28482         
28483         Roo.each(['day', 'month', 'year'], function(t){
28484             cfg.cn.push({
28485                 tag : 'div',
28486                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28487             });
28488         }, this);
28489         
28490         return cfg;
28491     },
28492     
28493     inputEl: function ()
28494     {
28495         return this.el.select('.roo-date-split-field-group-value', true).first();
28496     },
28497     
28498     onRender : function(ct, position) 
28499     {
28500         var _this = this;
28501         
28502         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28503         
28504         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28505         
28506         this.dayField = new Roo.bootstrap.ComboBox({
28507             allowBlank : this.dayAllowBlank,
28508             alwaysQuery : true,
28509             displayField : 'value',
28510             editable : false,
28511             fieldLabel : '',
28512             forceSelection : true,
28513             mode : 'local',
28514             placeholder : this.dayPlaceholder,
28515             selectOnFocus : true,
28516             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28517             triggerAction : 'all',
28518             typeAhead : true,
28519             valueField : 'value',
28520             store : new Roo.data.SimpleStore({
28521                 data : (function() {    
28522                     var days = [];
28523                     _this.fireEvent('days', _this, days);
28524                     return days;
28525                 })(),
28526                 fields : [ 'value' ]
28527             }),
28528             listeners : {
28529                 select : function (_self, record, index)
28530                 {
28531                     _this.setValue(_this.getValue());
28532                 }
28533             }
28534         });
28535
28536         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28537         
28538         this.monthField = new Roo.bootstrap.MonthField({
28539             after : '<i class=\"fa fa-calendar\"></i>',
28540             allowBlank : this.monthAllowBlank,
28541             placeholder : this.monthPlaceholder,
28542             readOnly : true,
28543             listeners : {
28544                 render : function (_self)
28545                 {
28546                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28547                         e.preventDefault();
28548                         _self.focus();
28549                     });
28550                 },
28551                 select : function (_self, oldvalue, newvalue)
28552                 {
28553                     _this.setValue(_this.getValue());
28554                 }
28555             }
28556         });
28557         
28558         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28559         
28560         this.yearField = new Roo.bootstrap.ComboBox({
28561             allowBlank : this.yearAllowBlank,
28562             alwaysQuery : true,
28563             displayField : 'value',
28564             editable : false,
28565             fieldLabel : '',
28566             forceSelection : true,
28567             mode : 'local',
28568             placeholder : this.yearPlaceholder,
28569             selectOnFocus : true,
28570             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28571             triggerAction : 'all',
28572             typeAhead : true,
28573             valueField : 'value',
28574             store : new Roo.data.SimpleStore({
28575                 data : (function() {
28576                     var years = [];
28577                     _this.fireEvent('years', _this, years);
28578                     return years;
28579                 })(),
28580                 fields : [ 'value' ]
28581             }),
28582             listeners : {
28583                 select : function (_self, record, index)
28584                 {
28585                     _this.setValue(_this.getValue());
28586                 }
28587             }
28588         });
28589
28590         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28591     },
28592     
28593     setValue : function(v, format)
28594     {
28595         this.inputEl.dom.value = v;
28596         
28597         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28598         
28599         var d = Date.parseDate(v, f);
28600         
28601         if(!d){
28602             this.validate();
28603             return;
28604         }
28605         
28606         this.setDay(d.format(this.dayFormat));
28607         this.setMonth(d.format(this.monthFormat));
28608         this.setYear(d.format(this.yearFormat));
28609         
28610         this.validate();
28611         
28612         return;
28613     },
28614     
28615     setDay : function(v)
28616     {
28617         this.dayField.setValue(v);
28618         this.inputEl.dom.value = this.getValue();
28619         this.validate();
28620         return;
28621     },
28622     
28623     setMonth : function(v)
28624     {
28625         this.monthField.setValue(v, true);
28626         this.inputEl.dom.value = this.getValue();
28627         this.validate();
28628         return;
28629     },
28630     
28631     setYear : function(v)
28632     {
28633         this.yearField.setValue(v);
28634         this.inputEl.dom.value = this.getValue();
28635         this.validate();
28636         return;
28637     },
28638     
28639     getDay : function()
28640     {
28641         return this.dayField.getValue();
28642     },
28643     
28644     getMonth : function()
28645     {
28646         return this.monthField.getValue();
28647     },
28648     
28649     getYear : function()
28650     {
28651         return this.yearField.getValue();
28652     },
28653     
28654     getValue : function()
28655     {
28656         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28657         
28658         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28659         
28660         return date;
28661     },
28662     
28663     reset : function()
28664     {
28665         this.setDay('');
28666         this.setMonth('');
28667         this.setYear('');
28668         this.inputEl.dom.value = '';
28669         this.validate();
28670         return;
28671     },
28672     
28673     validate : function()
28674     {
28675         var d = this.dayField.validate();
28676         var m = this.monthField.validate();
28677         var y = this.yearField.validate();
28678         
28679         var valid = true;
28680         
28681         if(
28682                 (!this.dayAllowBlank && !d) ||
28683                 (!this.monthAllowBlank && !m) ||
28684                 (!this.yearAllowBlank && !y)
28685         ){
28686             valid = false;
28687         }
28688         
28689         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28690             return valid;
28691         }
28692         
28693         if(valid){
28694             this.markValid();
28695             return valid;
28696         }
28697         
28698         this.markInvalid();
28699         
28700         return valid;
28701     },
28702     
28703     markValid : function()
28704     {
28705         
28706         var label = this.el.select('label', true).first();
28707         var icon = this.el.select('i.fa-star', true).first();
28708
28709         if(label && icon){
28710             icon.remove();
28711         }
28712         
28713         this.fireEvent('valid', this);
28714     },
28715     
28716      /**
28717      * Mark this field as invalid
28718      * @param {String} msg The validation message
28719      */
28720     markInvalid : function(msg)
28721     {
28722         
28723         var label = this.el.select('label', true).first();
28724         var icon = this.el.select('i.fa-star', true).first();
28725
28726         if(label && !icon){
28727             this.el.select('.roo-date-split-field-label', true).createChild({
28728                 tag : 'i',
28729                 cls : 'text-danger fa fa-lg fa-star',
28730                 tooltip : 'This field is required',
28731                 style : 'margin-right:5px;'
28732             }, label, true);
28733         }
28734         
28735         this.fireEvent('invalid', this, msg);
28736     },
28737     
28738     clearInvalid : function()
28739     {
28740         var label = this.el.select('label', true).first();
28741         var icon = this.el.select('i.fa-star', true).first();
28742
28743         if(label && icon){
28744             icon.remove();
28745         }
28746         
28747         this.fireEvent('valid', this);
28748     },
28749     
28750     getName: function()
28751     {
28752         return this.name;
28753     }
28754     
28755 });
28756
28757  /**
28758  *
28759  * This is based on 
28760  * http://masonry.desandro.com
28761  *
28762  * The idea is to render all the bricks based on vertical width...
28763  *
28764  * The original code extends 'outlayer' - we might need to use that....
28765  * 
28766  */
28767
28768
28769 /**
28770  * @class Roo.bootstrap.LayoutMasonry
28771  * @extends Roo.bootstrap.Component
28772  * Bootstrap Layout Masonry class
28773  * 
28774  * @constructor
28775  * Create a new Element
28776  * @param {Object} config The config object
28777  */
28778
28779 Roo.bootstrap.LayoutMasonry = function(config){
28780     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28781     
28782     this.bricks = [];
28783     
28784 };
28785
28786 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28787     
28788     /**
28789      * @cfg {Boolean} isLayoutInstant = no animation?
28790      */   
28791     isLayoutInstant : false, // needed?
28792    
28793     /**
28794      * @cfg {Number} boxWidth  width of the columns
28795      */   
28796     boxWidth : 450,
28797     
28798       /**
28799      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28800      */   
28801     boxHeight : 0,
28802     
28803     /**
28804      * @cfg {Number} padWidth padding below box..
28805      */   
28806     padWidth : 10, 
28807     
28808     /**
28809      * @cfg {Number} gutter gutter width..
28810      */   
28811     gutter : 10,
28812     
28813      /**
28814      * @cfg {Number} maxCols maximum number of columns
28815      */   
28816     
28817     maxCols: 0,
28818     
28819     /**
28820      * @cfg {Boolean} isAutoInitial defalut true
28821      */   
28822     isAutoInitial : true, 
28823     
28824     containerWidth: 0,
28825     
28826     /**
28827      * @cfg {Boolean} isHorizontal defalut false
28828      */   
28829     isHorizontal : false, 
28830
28831     currentSize : null,
28832     
28833     tag: 'div',
28834     
28835     cls: '',
28836     
28837     bricks: null, //CompositeElement
28838     
28839     cols : 1,
28840     
28841     _isLayoutInited : false,
28842     
28843 //    isAlternative : false, // only use for vertical layout...
28844     
28845     /**
28846      * @cfg {Number} alternativePadWidth padding below box..
28847      */   
28848     alternativePadWidth : 50, 
28849     
28850     getAutoCreate : function(){
28851         
28852         var cfg = {
28853             tag: this.tag,
28854             cls: 'blog-masonary-wrapper ' + this.cls,
28855             cn : {
28856                 cls : 'mas-boxes masonary'
28857             }
28858         };
28859         
28860         return cfg;
28861     },
28862     
28863     getChildContainer: function( )
28864     {
28865         if (this.boxesEl) {
28866             return this.boxesEl;
28867         }
28868         
28869         this.boxesEl = this.el.select('.mas-boxes').first();
28870         
28871         return this.boxesEl;
28872     },
28873     
28874     
28875     initEvents : function()
28876     {
28877         var _this = this;
28878         
28879         if(this.isAutoInitial){
28880             Roo.log('hook children rendered');
28881             this.on('childrenrendered', function() {
28882                 Roo.log('children rendered');
28883                 _this.initial();
28884             } ,this);
28885         }
28886     },
28887     
28888     initial : function()
28889     {
28890         this.currentSize = this.el.getBox(true);
28891         
28892         Roo.EventManager.onWindowResize(this.resize, this); 
28893
28894         if(!this.isAutoInitial){
28895             this.layout();
28896             return;
28897         }
28898         
28899         this.layout();
28900         
28901         return;
28902         //this.layout.defer(500,this);
28903         
28904     },
28905     
28906     resize : function()
28907     {
28908         Roo.log('resize');
28909         
28910         var cs = this.el.getBox(true);
28911         
28912         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28913             Roo.log("no change in with or X");
28914             return;
28915         }
28916         
28917         this.currentSize = cs;
28918         
28919         this.layout();
28920         
28921     },
28922     
28923     layout : function()
28924     {   
28925         this._resetLayout();
28926         
28927         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28928         
28929         this.layoutItems( isInstant );
28930       
28931         this._isLayoutInited = true;
28932         
28933     },
28934     
28935     _resetLayout : function()
28936     {
28937         if(this.isHorizontal){
28938             this.horizontalMeasureColumns();
28939             return;
28940         }
28941         
28942         this.verticalMeasureColumns();
28943         
28944     },
28945     
28946     verticalMeasureColumns : function()
28947     {
28948         this.getContainerWidth();
28949         
28950 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28951 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28952 //            return;
28953 //        }
28954         
28955         var boxWidth = this.boxWidth + this.padWidth;
28956         
28957         if(this.containerWidth < this.boxWidth){
28958             boxWidth = this.containerWidth
28959         }
28960         
28961         var containerWidth = this.containerWidth;
28962         
28963         var cols = Math.floor(containerWidth / boxWidth);
28964         
28965         this.cols = Math.max( cols, 1 );
28966         
28967         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28968         
28969         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28970         
28971         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28972         
28973         this.colWidth = boxWidth + avail - this.padWidth;
28974         
28975         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28976         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28977     },
28978     
28979     horizontalMeasureColumns : function()
28980     {
28981         this.getContainerWidth();
28982         
28983         var boxWidth = this.boxWidth;
28984         
28985         if(this.containerWidth < boxWidth){
28986             boxWidth = this.containerWidth;
28987         }
28988         
28989         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28990         
28991         this.el.setHeight(boxWidth);
28992         
28993     },
28994     
28995     getContainerWidth : function()
28996     {
28997         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28998     },
28999     
29000     layoutItems : function( isInstant )
29001     {
29002         var items = Roo.apply([], this.bricks);
29003         
29004         if(this.isHorizontal){
29005             this._horizontalLayoutItems( items , isInstant );
29006             return;
29007         }
29008         
29009 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29010 //            this._verticalAlternativeLayoutItems( items , isInstant );
29011 //            return;
29012 //        }
29013         
29014         this._verticalLayoutItems( items , isInstant );
29015         
29016     },
29017     
29018     _verticalLayoutItems : function ( items , isInstant)
29019     {
29020         if ( !items || !items.length ) {
29021             return;
29022         }
29023         
29024         var standard = [
29025             ['xs', 'xs', 'xs', 'tall'],
29026             ['xs', 'xs', 'tall'],
29027             ['xs', 'xs', 'sm'],
29028             ['xs', 'xs', 'xs'],
29029             ['xs', 'tall'],
29030             ['xs', 'sm'],
29031             ['xs', 'xs'],
29032             ['xs'],
29033             
29034             ['sm', 'xs', 'xs'],
29035             ['sm', 'xs'],
29036             ['sm'],
29037             
29038             ['tall', 'xs', 'xs', 'xs'],
29039             ['tall', 'xs', 'xs'],
29040             ['tall', 'xs'],
29041             ['tall']
29042             
29043         ];
29044         
29045         var queue = [];
29046         
29047         var boxes = [];
29048         
29049         var box = [];
29050         
29051         Roo.each(items, function(item, k){
29052             
29053             switch (item.size) {
29054                 // these layouts take up a full box,
29055                 case 'md' :
29056                 case 'md-left' :
29057                 case 'md-right' :
29058                 case 'wide' :
29059                     
29060                     if(box.length){
29061                         boxes.push(box);
29062                         box = [];
29063                     }
29064                     
29065                     boxes.push([item]);
29066                     
29067                     break;
29068                     
29069                 case 'xs' :
29070                 case 'sm' :
29071                 case 'tall' :
29072                     
29073                     box.push(item);
29074                     
29075                     break;
29076                 default :
29077                     break;
29078                     
29079             }
29080             
29081         }, this);
29082         
29083         if(box.length){
29084             boxes.push(box);
29085             box = [];
29086         }
29087         
29088         var filterPattern = function(box, length)
29089         {
29090             if(!box.length){
29091                 return;
29092             }
29093             
29094             var match = false;
29095             
29096             var pattern = box.slice(0, length);
29097             
29098             var format = [];
29099             
29100             Roo.each(pattern, function(i){
29101                 format.push(i.size);
29102             }, this);
29103             
29104             Roo.each(standard, function(s){
29105                 
29106                 if(String(s) != String(format)){
29107                     return;
29108                 }
29109                 
29110                 match = true;
29111                 return false;
29112                 
29113             }, this);
29114             
29115             if(!match && length == 1){
29116                 return;
29117             }
29118             
29119             if(!match){
29120                 filterPattern(box, length - 1);
29121                 return;
29122             }
29123                 
29124             queue.push(pattern);
29125
29126             box = box.slice(length, box.length);
29127
29128             filterPattern(box, 4);
29129
29130             return;
29131             
29132         }
29133         
29134         Roo.each(boxes, function(box, k){
29135             
29136             if(!box.length){
29137                 return;
29138             }
29139             
29140             if(box.length == 1){
29141                 queue.push(box);
29142                 return;
29143             }
29144             
29145             filterPattern(box, 4);
29146             
29147         }, this);
29148         
29149         this._processVerticalLayoutQueue( queue, isInstant );
29150         
29151     },
29152     
29153 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29154 //    {
29155 //        if ( !items || !items.length ) {
29156 //            return;
29157 //        }
29158 //
29159 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29160 //        
29161 //    },
29162     
29163     _horizontalLayoutItems : function ( items , isInstant)
29164     {
29165         if ( !items || !items.length || items.length < 3) {
29166             return;
29167         }
29168         
29169         items.reverse();
29170         
29171         var eItems = items.slice(0, 3);
29172         
29173         items = items.slice(3, items.length);
29174         
29175         var standard = [
29176             ['xs', 'xs', 'xs', 'wide'],
29177             ['xs', 'xs', 'wide'],
29178             ['xs', 'xs', 'sm'],
29179             ['xs', 'xs', 'xs'],
29180             ['xs', 'wide'],
29181             ['xs', 'sm'],
29182             ['xs', 'xs'],
29183             ['xs'],
29184             
29185             ['sm', 'xs', 'xs'],
29186             ['sm', 'xs'],
29187             ['sm'],
29188             
29189             ['wide', 'xs', 'xs', 'xs'],
29190             ['wide', 'xs', 'xs'],
29191             ['wide', 'xs'],
29192             ['wide'],
29193             
29194             ['wide-thin']
29195         ];
29196         
29197         var queue = [];
29198         
29199         var boxes = [];
29200         
29201         var box = [];
29202         
29203         Roo.each(items, function(item, k){
29204             
29205             switch (item.size) {
29206                 case 'md' :
29207                 case 'md-left' :
29208                 case 'md-right' :
29209                 case 'tall' :
29210                     
29211                     if(box.length){
29212                         boxes.push(box);
29213                         box = [];
29214                     }
29215                     
29216                     boxes.push([item]);
29217                     
29218                     break;
29219                     
29220                 case 'xs' :
29221                 case 'sm' :
29222                 case 'wide' :
29223                 case 'wide-thin' :
29224                     
29225                     box.push(item);
29226                     
29227                     break;
29228                 default :
29229                     break;
29230                     
29231             }
29232             
29233         }, this);
29234         
29235         if(box.length){
29236             boxes.push(box);
29237             box = [];
29238         }
29239         
29240         var filterPattern = function(box, length)
29241         {
29242             if(!box.length){
29243                 return;
29244             }
29245             
29246             var match = false;
29247             
29248             var pattern = box.slice(0, length);
29249             
29250             var format = [];
29251             
29252             Roo.each(pattern, function(i){
29253                 format.push(i.size);
29254             }, this);
29255             
29256             Roo.each(standard, function(s){
29257                 
29258                 if(String(s) != String(format)){
29259                     return;
29260                 }
29261                 
29262                 match = true;
29263                 return false;
29264                 
29265             }, this);
29266             
29267             if(!match && length == 1){
29268                 return;
29269             }
29270             
29271             if(!match){
29272                 filterPattern(box, length - 1);
29273                 return;
29274             }
29275                 
29276             queue.push(pattern);
29277
29278             box = box.slice(length, box.length);
29279
29280             filterPattern(box, 4);
29281
29282             return;
29283             
29284         }
29285         
29286         Roo.each(boxes, function(box, k){
29287             
29288             if(!box.length){
29289                 return;
29290             }
29291             
29292             if(box.length == 1){
29293                 queue.push(box);
29294                 return;
29295             }
29296             
29297             filterPattern(box, 4);
29298             
29299         }, this);
29300         
29301         
29302         var prune = [];
29303         
29304         var pos = this.el.getBox(true);
29305         
29306         var minX = pos.x;
29307         
29308         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29309         
29310         var hit_end = false;
29311         
29312         Roo.each(queue, function(box){
29313             
29314             if(hit_end){
29315                 
29316                 Roo.each(box, function(b){
29317                 
29318                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29319                     b.el.hide();
29320
29321                 }, this);
29322
29323                 return;
29324             }
29325             
29326             var mx = 0;
29327             
29328             Roo.each(box, function(b){
29329                 
29330                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29331                 b.el.show();
29332
29333                 mx = Math.max(mx, b.x);
29334                 
29335             }, this);
29336             
29337             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29338             
29339             if(maxX < minX){
29340                 
29341                 Roo.each(box, function(b){
29342                 
29343                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29344                     b.el.hide();
29345                     
29346                 }, this);
29347                 
29348                 hit_end = true;
29349                 
29350                 return;
29351             }
29352             
29353             prune.push(box);
29354             
29355         }, this);
29356         
29357         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29358     },
29359     
29360     /** Sets position of item in DOM
29361     * @param {Element} item
29362     * @param {Number} x - horizontal position
29363     * @param {Number} y - vertical position
29364     * @param {Boolean} isInstant - disables transitions
29365     */
29366     _processVerticalLayoutQueue : function( queue, isInstant )
29367     {
29368         var pos = this.el.getBox(true);
29369         var x = pos.x;
29370         var y = pos.y;
29371         var maxY = [];
29372         
29373         for (var i = 0; i < this.cols; i++){
29374             maxY[i] = pos.y;
29375         }
29376         
29377         Roo.each(queue, function(box, k){
29378             
29379             var col = k % this.cols;
29380             
29381             Roo.each(box, function(b,kk){
29382                 
29383                 b.el.position('absolute');
29384                 
29385                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29386                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29387                 
29388                 if(b.size == 'md-left' || b.size == 'md-right'){
29389                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29390                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29391                 }
29392                 
29393                 b.el.setWidth(width);
29394                 b.el.setHeight(height);
29395                 // iframe?
29396                 b.el.select('iframe',true).setSize(width,height);
29397                 
29398             }, this);
29399             
29400             for (var i = 0; i < this.cols; i++){
29401                 
29402                 if(maxY[i] < maxY[col]){
29403                     col = i;
29404                     continue;
29405                 }
29406                 
29407                 col = Math.min(col, i);
29408                 
29409             }
29410             
29411             x = pos.x + col * (this.colWidth + this.padWidth);
29412             
29413             y = maxY[col];
29414             
29415             var positions = [];
29416             
29417             switch (box.length){
29418                 case 1 :
29419                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29420                     break;
29421                 case 2 :
29422                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29423                     break;
29424                 case 3 :
29425                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29426                     break;
29427                 case 4 :
29428                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29429                     break;
29430                 default :
29431                     break;
29432             }
29433             
29434             Roo.each(box, function(b,kk){
29435                 
29436                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29437                 
29438                 var sz = b.el.getSize();
29439                 
29440                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29441                 
29442             }, this);
29443             
29444         }, this);
29445         
29446         var mY = 0;
29447         
29448         for (var i = 0; i < this.cols; i++){
29449             mY = Math.max(mY, maxY[i]);
29450         }
29451         
29452         this.el.setHeight(mY - pos.y);
29453         
29454     },
29455     
29456 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29457 //    {
29458 //        var pos = this.el.getBox(true);
29459 //        var x = pos.x;
29460 //        var y = pos.y;
29461 //        var maxX = pos.right;
29462 //        
29463 //        var maxHeight = 0;
29464 //        
29465 //        Roo.each(items, function(item, k){
29466 //            
29467 //            var c = k % 2;
29468 //            
29469 //            item.el.position('absolute');
29470 //                
29471 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29472 //
29473 //            item.el.setWidth(width);
29474 //
29475 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29476 //
29477 //            item.el.setHeight(height);
29478 //            
29479 //            if(c == 0){
29480 //                item.el.setXY([x, y], isInstant ? false : true);
29481 //            } else {
29482 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29483 //            }
29484 //            
29485 //            y = y + height + this.alternativePadWidth;
29486 //            
29487 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29488 //            
29489 //        }, this);
29490 //        
29491 //        this.el.setHeight(maxHeight);
29492 //        
29493 //    },
29494     
29495     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29496     {
29497         var pos = this.el.getBox(true);
29498         
29499         var minX = pos.x;
29500         var minY = pos.y;
29501         
29502         var maxX = pos.right;
29503         
29504         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29505         
29506         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29507         
29508         Roo.each(queue, function(box, k){
29509             
29510             Roo.each(box, function(b, kk){
29511                 
29512                 b.el.position('absolute');
29513                 
29514                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29515                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29516                 
29517                 if(b.size == 'md-left' || b.size == 'md-right'){
29518                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29519                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29520                 }
29521                 
29522                 b.el.setWidth(width);
29523                 b.el.setHeight(height);
29524                 
29525             }, this);
29526             
29527             if(!box.length){
29528                 return;
29529             }
29530             
29531             var positions = [];
29532             
29533             switch (box.length){
29534                 case 1 :
29535                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29536                     break;
29537                 case 2 :
29538                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29539                     break;
29540                 case 3 :
29541                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29542                     break;
29543                 case 4 :
29544                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29545                     break;
29546                 default :
29547                     break;
29548             }
29549             
29550             Roo.each(box, function(b,kk){
29551                 
29552                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29553                 
29554                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29555                 
29556             }, this);
29557             
29558         }, this);
29559         
29560     },
29561     
29562     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29563     {
29564         Roo.each(eItems, function(b,k){
29565             
29566             b.size = (k == 0) ? 'sm' : 'xs';
29567             b.x = (k == 0) ? 2 : 1;
29568             b.y = (k == 0) ? 2 : 1;
29569             
29570             b.el.position('absolute');
29571             
29572             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29573                 
29574             b.el.setWidth(width);
29575             
29576             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29577             
29578             b.el.setHeight(height);
29579             
29580         }, this);
29581
29582         var positions = [];
29583         
29584         positions.push({
29585             x : maxX - this.unitWidth * 2 - this.gutter,
29586             y : minY
29587         });
29588         
29589         positions.push({
29590             x : maxX - this.unitWidth,
29591             y : minY + (this.unitWidth + this.gutter) * 2
29592         });
29593         
29594         positions.push({
29595             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29596             y : minY
29597         });
29598         
29599         Roo.each(eItems, function(b,k){
29600             
29601             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29602
29603         }, this);
29604         
29605     },
29606     
29607     getVerticalOneBoxColPositions : function(x, y, box)
29608     {
29609         var pos = [];
29610         
29611         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29612         
29613         if(box[0].size == 'md-left'){
29614             rand = 0;
29615         }
29616         
29617         if(box[0].size == 'md-right'){
29618             rand = 1;
29619         }
29620         
29621         pos.push({
29622             x : x + (this.unitWidth + this.gutter) * rand,
29623             y : y
29624         });
29625         
29626         return pos;
29627     },
29628     
29629     getVerticalTwoBoxColPositions : function(x, y, box)
29630     {
29631         var pos = [];
29632         
29633         if(box[0].size == 'xs'){
29634             
29635             pos.push({
29636                 x : x,
29637                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29638             });
29639
29640             pos.push({
29641                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29642                 y : y
29643             });
29644             
29645             return pos;
29646             
29647         }
29648         
29649         pos.push({
29650             x : x,
29651             y : y
29652         });
29653
29654         pos.push({
29655             x : x + (this.unitWidth + this.gutter) * 2,
29656             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29657         });
29658         
29659         return pos;
29660         
29661     },
29662     
29663     getVerticalThreeBoxColPositions : function(x, y, box)
29664     {
29665         var pos = [];
29666         
29667         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29668             
29669             pos.push({
29670                 x : x,
29671                 y : y
29672             });
29673
29674             pos.push({
29675                 x : x + (this.unitWidth + this.gutter) * 1,
29676                 y : y
29677             });
29678             
29679             pos.push({
29680                 x : x + (this.unitWidth + this.gutter) * 2,
29681                 y : y
29682             });
29683             
29684             return pos;
29685             
29686         }
29687         
29688         if(box[0].size == 'xs' && box[1].size == 'xs'){
29689             
29690             pos.push({
29691                 x : x,
29692                 y : y
29693             });
29694
29695             pos.push({
29696                 x : x,
29697                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29698             });
29699             
29700             pos.push({
29701                 x : x + (this.unitWidth + this.gutter) * 1,
29702                 y : y
29703             });
29704             
29705             return pos;
29706             
29707         }
29708         
29709         pos.push({
29710             x : x,
29711             y : y
29712         });
29713
29714         pos.push({
29715             x : x + (this.unitWidth + this.gutter) * 2,
29716             y : y
29717         });
29718
29719         pos.push({
29720             x : x + (this.unitWidth + this.gutter) * 2,
29721             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29722         });
29723             
29724         return pos;
29725         
29726     },
29727     
29728     getVerticalFourBoxColPositions : function(x, y, box)
29729     {
29730         var pos = [];
29731         
29732         if(box[0].size == 'xs'){
29733             
29734             pos.push({
29735                 x : x,
29736                 y : y
29737             });
29738
29739             pos.push({
29740                 x : x,
29741                 y : y + (this.unitHeight + this.gutter) * 1
29742             });
29743             
29744             pos.push({
29745                 x : x,
29746                 y : y + (this.unitHeight + this.gutter) * 2
29747             });
29748             
29749             pos.push({
29750                 x : x + (this.unitWidth + this.gutter) * 1,
29751                 y : y
29752             });
29753             
29754             return pos;
29755             
29756         }
29757         
29758         pos.push({
29759             x : x,
29760             y : y
29761         });
29762
29763         pos.push({
29764             x : x + (this.unitWidth + this.gutter) * 2,
29765             y : y
29766         });
29767
29768         pos.push({
29769             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29770             y : y + (this.unitHeight + this.gutter) * 1
29771         });
29772
29773         pos.push({
29774             x : x + (this.unitWidth + this.gutter) * 2,
29775             y : y + (this.unitWidth + this.gutter) * 2
29776         });
29777
29778         return pos;
29779         
29780     },
29781     
29782     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29783     {
29784         var pos = [];
29785         
29786         if(box[0].size == 'md-left'){
29787             pos.push({
29788                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29789                 y : minY
29790             });
29791             
29792             return pos;
29793         }
29794         
29795         if(box[0].size == 'md-right'){
29796             pos.push({
29797                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29798                 y : minY + (this.unitWidth + this.gutter) * 1
29799             });
29800             
29801             return pos;
29802         }
29803         
29804         var rand = Math.floor(Math.random() * (4 - box[0].y));
29805         
29806         pos.push({
29807             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29808             y : minY + (this.unitWidth + this.gutter) * rand
29809         });
29810         
29811         return pos;
29812         
29813     },
29814     
29815     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29816     {
29817         var pos = [];
29818         
29819         if(box[0].size == 'xs'){
29820             
29821             pos.push({
29822                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29823                 y : minY
29824             });
29825
29826             pos.push({
29827                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29828                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29829             });
29830             
29831             return pos;
29832             
29833         }
29834         
29835         pos.push({
29836             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29837             y : minY
29838         });
29839
29840         pos.push({
29841             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29842             y : minY + (this.unitWidth + this.gutter) * 2
29843         });
29844         
29845         return pos;
29846         
29847     },
29848     
29849     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29850     {
29851         var pos = [];
29852         
29853         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29854             
29855             pos.push({
29856                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29857                 y : minY
29858             });
29859
29860             pos.push({
29861                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29862                 y : minY + (this.unitWidth + this.gutter) * 1
29863             });
29864             
29865             pos.push({
29866                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29867                 y : minY + (this.unitWidth + this.gutter) * 2
29868             });
29869             
29870             return pos;
29871             
29872         }
29873         
29874         if(box[0].size == 'xs' && box[1].size == 'xs'){
29875             
29876             pos.push({
29877                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29878                 y : minY
29879             });
29880
29881             pos.push({
29882                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29883                 y : minY
29884             });
29885             
29886             pos.push({
29887                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29888                 y : minY + (this.unitWidth + this.gutter) * 1
29889             });
29890             
29891             return pos;
29892             
29893         }
29894         
29895         pos.push({
29896             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29897             y : minY
29898         });
29899
29900         pos.push({
29901             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29902             y : minY + (this.unitWidth + this.gutter) * 2
29903         });
29904
29905         pos.push({
29906             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29907             y : minY + (this.unitWidth + this.gutter) * 2
29908         });
29909             
29910         return pos;
29911         
29912     },
29913     
29914     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29915     {
29916         var pos = [];
29917         
29918         if(box[0].size == 'xs'){
29919             
29920             pos.push({
29921                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29922                 y : minY
29923             });
29924
29925             pos.push({
29926                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29927                 y : minY
29928             });
29929             
29930             pos.push({
29931                 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),
29932                 y : minY
29933             });
29934             
29935             pos.push({
29936                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29937                 y : minY + (this.unitWidth + this.gutter) * 1
29938             });
29939             
29940             return pos;
29941             
29942         }
29943         
29944         pos.push({
29945             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29946             y : minY
29947         });
29948         
29949         pos.push({
29950             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29951             y : minY + (this.unitWidth + this.gutter) * 2
29952         });
29953         
29954         pos.push({
29955             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29956             y : minY + (this.unitWidth + this.gutter) * 2
29957         });
29958         
29959         pos.push({
29960             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),
29961             y : minY + (this.unitWidth + this.gutter) * 2
29962         });
29963
29964         return pos;
29965         
29966     }
29967     
29968 });
29969
29970  
29971
29972  /**
29973  *
29974  * This is based on 
29975  * http://masonry.desandro.com
29976  *
29977  * The idea is to render all the bricks based on vertical width...
29978  *
29979  * The original code extends 'outlayer' - we might need to use that....
29980  * 
29981  */
29982
29983
29984 /**
29985  * @class Roo.bootstrap.LayoutMasonryAuto
29986  * @extends Roo.bootstrap.Component
29987  * Bootstrap Layout Masonry class
29988  * 
29989  * @constructor
29990  * Create a new Element
29991  * @param {Object} config The config object
29992  */
29993
29994 Roo.bootstrap.LayoutMasonryAuto = function(config){
29995     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29996 };
29997
29998 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29999     
30000       /**
30001      * @cfg {Boolean} isFitWidth  - resize the width..
30002      */   
30003     isFitWidth : false,  // options..
30004     /**
30005      * @cfg {Boolean} isOriginLeft = left align?
30006      */   
30007     isOriginLeft : true,
30008     /**
30009      * @cfg {Boolean} isOriginTop = top align?
30010      */   
30011     isOriginTop : false,
30012     /**
30013      * @cfg {Boolean} isLayoutInstant = no animation?
30014      */   
30015     isLayoutInstant : false, // needed?
30016     /**
30017      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30018      */   
30019     isResizingContainer : true,
30020     /**
30021      * @cfg {Number} columnWidth  width of the columns 
30022      */   
30023     
30024     columnWidth : 0,
30025     
30026     /**
30027      * @cfg {Number} maxCols maximum number of columns
30028      */   
30029     
30030     maxCols: 0,
30031     /**
30032      * @cfg {Number} padHeight padding below box..
30033      */   
30034     
30035     padHeight : 10, 
30036     
30037     /**
30038      * @cfg {Boolean} isAutoInitial defalut true
30039      */   
30040     
30041     isAutoInitial : true, 
30042     
30043     // private?
30044     gutter : 0,
30045     
30046     containerWidth: 0,
30047     initialColumnWidth : 0,
30048     currentSize : null,
30049     
30050     colYs : null, // array.
30051     maxY : 0,
30052     padWidth: 10,
30053     
30054     
30055     tag: 'div',
30056     cls: '',
30057     bricks: null, //CompositeElement
30058     cols : 0, // array?
30059     // element : null, // wrapped now this.el
30060     _isLayoutInited : null, 
30061     
30062     
30063     getAutoCreate : function(){
30064         
30065         var cfg = {
30066             tag: this.tag,
30067             cls: 'blog-masonary-wrapper ' + this.cls,
30068             cn : {
30069                 cls : 'mas-boxes masonary'
30070             }
30071         };
30072         
30073         return cfg;
30074     },
30075     
30076     getChildContainer: function( )
30077     {
30078         if (this.boxesEl) {
30079             return this.boxesEl;
30080         }
30081         
30082         this.boxesEl = this.el.select('.mas-boxes').first();
30083         
30084         return this.boxesEl;
30085     },
30086     
30087     
30088     initEvents : function()
30089     {
30090         var _this = this;
30091         
30092         if(this.isAutoInitial){
30093             Roo.log('hook children rendered');
30094             this.on('childrenrendered', function() {
30095                 Roo.log('children rendered');
30096                 _this.initial();
30097             } ,this);
30098         }
30099         
30100     },
30101     
30102     initial : function()
30103     {
30104         this.reloadItems();
30105
30106         this.currentSize = this.el.getBox(true);
30107
30108         /// was window resize... - let's see if this works..
30109         Roo.EventManager.onWindowResize(this.resize, this); 
30110
30111         if(!this.isAutoInitial){
30112             this.layout();
30113             return;
30114         }
30115         
30116         this.layout.defer(500,this);
30117     },
30118     
30119     reloadItems: function()
30120     {
30121         this.bricks = this.el.select('.masonry-brick', true);
30122         
30123         this.bricks.each(function(b) {
30124             //Roo.log(b.getSize());
30125             if (!b.attr('originalwidth')) {
30126                 b.attr('originalwidth',  b.getSize().width);
30127             }
30128             
30129         });
30130         
30131         Roo.log(this.bricks.elements.length);
30132     },
30133     
30134     resize : function()
30135     {
30136         Roo.log('resize');
30137         var cs = this.el.getBox(true);
30138         
30139         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30140             Roo.log("no change in with or X");
30141             return;
30142         }
30143         this.currentSize = cs;
30144         this.layout();
30145     },
30146     
30147     layout : function()
30148     {
30149          Roo.log('layout');
30150         this._resetLayout();
30151         //this._manageStamps();
30152       
30153         // don't animate first layout
30154         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30155         this.layoutItems( isInstant );
30156       
30157         // flag for initalized
30158         this._isLayoutInited = true;
30159     },
30160     
30161     layoutItems : function( isInstant )
30162     {
30163         //var items = this._getItemsForLayout( this.items );
30164         // original code supports filtering layout items.. we just ignore it..
30165         
30166         this._layoutItems( this.bricks , isInstant );
30167       
30168         this._postLayout();
30169     },
30170     _layoutItems : function ( items , isInstant)
30171     {
30172        //this.fireEvent( 'layout', this, items );
30173     
30174
30175         if ( !items || !items.elements.length ) {
30176           // no items, emit event with empty array
30177             return;
30178         }
30179
30180         var queue = [];
30181         items.each(function(item) {
30182             Roo.log("layout item");
30183             Roo.log(item);
30184             // get x/y object from method
30185             var position = this._getItemLayoutPosition( item );
30186             // enqueue
30187             position.item = item;
30188             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30189             queue.push( position );
30190         }, this);
30191       
30192         this._processLayoutQueue( queue );
30193     },
30194     /** Sets position of item in DOM
30195     * @param {Element} item
30196     * @param {Number} x - horizontal position
30197     * @param {Number} y - vertical position
30198     * @param {Boolean} isInstant - disables transitions
30199     */
30200     _processLayoutQueue : function( queue )
30201     {
30202         for ( var i=0, len = queue.length; i < len; i++ ) {
30203             var obj = queue[i];
30204             obj.item.position('absolute');
30205             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30206         }
30207     },
30208       
30209     
30210     /**
30211     * Any logic you want to do after each layout,
30212     * i.e. size the container
30213     */
30214     _postLayout : function()
30215     {
30216         this.resizeContainer();
30217     },
30218     
30219     resizeContainer : function()
30220     {
30221         if ( !this.isResizingContainer ) {
30222             return;
30223         }
30224         var size = this._getContainerSize();
30225         if ( size ) {
30226             this.el.setSize(size.width,size.height);
30227             this.boxesEl.setSize(size.width,size.height);
30228         }
30229     },
30230     
30231     
30232     
30233     _resetLayout : function()
30234     {
30235         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30236         this.colWidth = this.el.getWidth();
30237         //this.gutter = this.el.getWidth(); 
30238         
30239         this.measureColumns();
30240
30241         // reset column Y
30242         var i = this.cols;
30243         this.colYs = [];
30244         while (i--) {
30245             this.colYs.push( 0 );
30246         }
30247     
30248         this.maxY = 0;
30249     },
30250
30251     measureColumns : function()
30252     {
30253         this.getContainerWidth();
30254       // if columnWidth is 0, default to outerWidth of first item
30255         if ( !this.columnWidth ) {
30256             var firstItem = this.bricks.first();
30257             Roo.log(firstItem);
30258             this.columnWidth  = this.containerWidth;
30259             if (firstItem && firstItem.attr('originalwidth') ) {
30260                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30261             }
30262             // columnWidth fall back to item of first element
30263             Roo.log("set column width?");
30264                         this.initialColumnWidth = this.columnWidth  ;
30265
30266             // if first elem has no width, default to size of container
30267             
30268         }
30269         
30270         
30271         if (this.initialColumnWidth) {
30272             this.columnWidth = this.initialColumnWidth;
30273         }
30274         
30275         
30276             
30277         // column width is fixed at the top - however if container width get's smaller we should
30278         // reduce it...
30279         
30280         // this bit calcs how man columns..
30281             
30282         var columnWidth = this.columnWidth += this.gutter;
30283       
30284         // calculate columns
30285         var containerWidth = this.containerWidth + this.gutter;
30286         
30287         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30288         // fix rounding errors, typically with gutters
30289         var excess = columnWidth - containerWidth % columnWidth;
30290         
30291         
30292         // if overshoot is less than a pixel, round up, otherwise floor it
30293         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30294         cols = Math[ mathMethod ]( cols );
30295         this.cols = Math.max( cols, 1 );
30296         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30297         
30298          // padding positioning..
30299         var totalColWidth = this.cols * this.columnWidth;
30300         var padavail = this.containerWidth - totalColWidth;
30301         // so for 2 columns - we need 3 'pads'
30302         
30303         var padNeeded = (1+this.cols) * this.padWidth;
30304         
30305         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30306         
30307         this.columnWidth += padExtra
30308         //this.padWidth = Math.floor(padavail /  ( this.cols));
30309         
30310         // adjust colum width so that padding is fixed??
30311         
30312         // we have 3 columns ... total = width * 3
30313         // we have X left over... that should be used by 
30314         
30315         //if (this.expandC) {
30316             
30317         //}
30318         
30319         
30320         
30321     },
30322     
30323     getContainerWidth : function()
30324     {
30325        /* // container is parent if fit width
30326         var container = this.isFitWidth ? this.element.parentNode : this.element;
30327         // check that this.size and size are there
30328         // IE8 triggers resize on body size change, so they might not be
30329         
30330         var size = getSize( container );  //FIXME
30331         this.containerWidth = size && size.innerWidth; //FIXME
30332         */
30333          
30334         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30335         
30336     },
30337     
30338     _getItemLayoutPosition : function( item )  // what is item?
30339     {
30340         // we resize the item to our columnWidth..
30341       
30342         item.setWidth(this.columnWidth);
30343         item.autoBoxAdjust  = false;
30344         
30345         var sz = item.getSize();
30346  
30347         // how many columns does this brick span
30348         var remainder = this.containerWidth % this.columnWidth;
30349         
30350         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30351         // round if off by 1 pixel, otherwise use ceil
30352         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30353         colSpan = Math.min( colSpan, this.cols );
30354         
30355         // normally this should be '1' as we dont' currently allow multi width columns..
30356         
30357         var colGroup = this._getColGroup( colSpan );
30358         // get the minimum Y value from the columns
30359         var minimumY = Math.min.apply( Math, colGroup );
30360         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30361         
30362         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30363          
30364         // position the brick
30365         var position = {
30366             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30367             y: this.currentSize.y + minimumY + this.padHeight
30368         };
30369         
30370         Roo.log(position);
30371         // apply setHeight to necessary columns
30372         var setHeight = minimumY + sz.height + this.padHeight;
30373         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30374         
30375         var setSpan = this.cols + 1 - colGroup.length;
30376         for ( var i = 0; i < setSpan; i++ ) {
30377           this.colYs[ shortColIndex + i ] = setHeight ;
30378         }
30379       
30380         return position;
30381     },
30382     
30383     /**
30384      * @param {Number} colSpan - number of columns the element spans
30385      * @returns {Array} colGroup
30386      */
30387     _getColGroup : function( colSpan )
30388     {
30389         if ( colSpan < 2 ) {
30390           // if brick spans only one column, use all the column Ys
30391           return this.colYs;
30392         }
30393       
30394         var colGroup = [];
30395         // how many different places could this brick fit horizontally
30396         var groupCount = this.cols + 1 - colSpan;
30397         // for each group potential horizontal position
30398         for ( var i = 0; i < groupCount; i++ ) {
30399           // make an array of colY values for that one group
30400           var groupColYs = this.colYs.slice( i, i + colSpan );
30401           // and get the max value of the array
30402           colGroup[i] = Math.max.apply( Math, groupColYs );
30403         }
30404         return colGroup;
30405     },
30406     /*
30407     _manageStamp : function( stamp )
30408     {
30409         var stampSize =  stamp.getSize();
30410         var offset = stamp.getBox();
30411         // get the columns that this stamp affects
30412         var firstX = this.isOriginLeft ? offset.x : offset.right;
30413         var lastX = firstX + stampSize.width;
30414         var firstCol = Math.floor( firstX / this.columnWidth );
30415         firstCol = Math.max( 0, firstCol );
30416         
30417         var lastCol = Math.floor( lastX / this.columnWidth );
30418         // lastCol should not go over if multiple of columnWidth #425
30419         lastCol -= lastX % this.columnWidth ? 0 : 1;
30420         lastCol = Math.min( this.cols - 1, lastCol );
30421         
30422         // set colYs to bottom of the stamp
30423         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30424             stampSize.height;
30425             
30426         for ( var i = firstCol; i <= lastCol; i++ ) {
30427           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30428         }
30429     },
30430     */
30431     
30432     _getContainerSize : function()
30433     {
30434         this.maxY = Math.max.apply( Math, this.colYs );
30435         var size = {
30436             height: this.maxY
30437         };
30438       
30439         if ( this.isFitWidth ) {
30440             size.width = this._getContainerFitWidth();
30441         }
30442       
30443         return size;
30444     },
30445     
30446     _getContainerFitWidth : function()
30447     {
30448         var unusedCols = 0;
30449         // count unused columns
30450         var i = this.cols;
30451         while ( --i ) {
30452           if ( this.colYs[i] !== 0 ) {
30453             break;
30454           }
30455           unusedCols++;
30456         }
30457         // fit container to columns that have been used
30458         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30459     },
30460     
30461     needsResizeLayout : function()
30462     {
30463         var previousWidth = this.containerWidth;
30464         this.getContainerWidth();
30465         return previousWidth !== this.containerWidth;
30466     }
30467  
30468 });
30469
30470  
30471
30472  /*
30473  * - LGPL
30474  *
30475  * element
30476  * 
30477  */
30478
30479 /**
30480  * @class Roo.bootstrap.MasonryBrick
30481  * @extends Roo.bootstrap.Component
30482  * Bootstrap MasonryBrick class
30483  * 
30484  * @constructor
30485  * Create a new MasonryBrick
30486  * @param {Object} config The config object
30487  */
30488
30489 Roo.bootstrap.MasonryBrick = function(config){
30490     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30491     
30492     this.addEvents({
30493         // raw events
30494         /**
30495          * @event click
30496          * When a MasonryBrick is clcik
30497          * @param {Roo.bootstrap.MasonryBrick} this
30498          * @param {Roo.EventObject} e
30499          */
30500         "click" : true
30501     });
30502 };
30503
30504 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30505     
30506     /**
30507      * @cfg {String} title
30508      */   
30509     title : '',
30510     /**
30511      * @cfg {String} html
30512      */   
30513     html : '',
30514     /**
30515      * @cfg {String} bgimage
30516      */   
30517     bgimage : '',
30518     /**
30519      * @cfg {String} videourl
30520      */   
30521     videourl : '',
30522     /**
30523      * @cfg {String} cls
30524      */   
30525     cls : '',
30526     /**
30527      * @cfg {String} href
30528      */   
30529     href : '',
30530     /**
30531      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30532      */   
30533     size : 'xs',
30534     
30535     /**
30536      * @cfg {String} (center|bottom) placetitle
30537      */   
30538     placetitle : '',
30539     
30540     getAutoCreate : function()
30541     {
30542         var cls = 'masonry-brick';
30543         
30544         if(this.href.length){
30545             cls += ' masonry-brick-link';
30546         }
30547         
30548         if(this.bgimage.length){
30549             cls += ' masonry-brick-image';
30550         }
30551         
30552         if(this.size){
30553             cls += ' masonry-' + this.size + '-brick';
30554         }
30555         
30556         if(this.placetitle.length){
30557             
30558             switch (this.placetitle) {
30559                 case 'center' :
30560                     cls += ' masonry-center-title';
30561                     break;
30562                 case 'bottom' :
30563                     cls += ' masonry-bottom-title';
30564                     break;
30565                 default:
30566                     break;
30567             }
30568             
30569         } else {
30570             if(!this.html.length && !this.bgimage.length){
30571                 cls += ' masonry-center-title';
30572             }
30573
30574             if(!this.html.length && this.bgimage.length){
30575                 cls += ' masonry-bottom-title';
30576             }
30577         }
30578         
30579         if(this.cls){
30580             cls += ' ' + this.cls;
30581         }
30582         
30583         var cfg = {
30584             tag: (this.href.length) ? 'a' : 'div',
30585             cls: cls,
30586             cn: [
30587                 {
30588                     tag: 'div',
30589                     cls: 'masonry-brick-paragraph',
30590                     cn: []
30591                 }
30592             ]
30593         };
30594         
30595         if(this.href.length){
30596             cfg.href = this.href;
30597         }
30598         
30599         var cn = cfg.cn[0].cn;
30600         
30601         if(this.title.length){
30602             cn.push({
30603                 tag: 'h4',
30604                 cls: 'masonry-brick-title',
30605                 html: this.title
30606             });
30607         }
30608         
30609         if(this.html.length){
30610             cn.push({
30611                 tag: 'p',
30612                 cls: 'masonry-brick-text',
30613                 html: this.html
30614             });
30615         }  
30616         if (!this.title.length && !this.html.length) {
30617             cfg.cn[0].cls += ' hide';
30618         }
30619         
30620         if(this.bgimage.length){
30621             cfg.cn.push({
30622                 tag: 'img',
30623                 cls: 'masonry-brick-image-view',
30624                 src: this.bgimage
30625             });
30626         }
30627         if(this.videourl.length){
30628             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30629             // youtube support only?
30630             cfg.cn.push({
30631                 tag: 'iframe',
30632                 cls: 'masonry-brick-image-view',
30633                 src: vurl,
30634                 frameborder : 0,
30635                 allowfullscreen : true
30636             });
30637             
30638             
30639         }
30640         return cfg;
30641         
30642     },
30643     
30644     initEvents: function() 
30645     {
30646         switch (this.size) {
30647             case 'xs' :
30648 //                this.intSize = 1;
30649                 this.x = 1;
30650                 this.y = 1;
30651                 break;
30652             case 'sm' :
30653 //                this.intSize = 2;
30654                 this.x = 2;
30655                 this.y = 2;
30656                 break;
30657             case 'md' :
30658             case 'md-left' :
30659             case 'md-right' :
30660 //                this.intSize = 3;
30661                 this.x = 3;
30662                 this.y = 3;
30663                 break;
30664             case 'tall' :
30665 //                this.intSize = 3;
30666                 this.x = 2;
30667                 this.y = 3;
30668                 break;
30669             case 'wide' :
30670 //                this.intSize = 3;
30671                 this.x = 3;
30672                 this.y = 2;
30673                 break;
30674             case 'wide-thin' :
30675 //                this.intSize = 3;
30676                 this.x = 3;
30677                 this.y = 1;
30678                 break;
30679                         
30680             default :
30681                 break;
30682         }
30683         
30684         
30685         
30686         if(Roo.isTouch){
30687             this.el.on('touchstart', this.onTouchStart, this);
30688             this.el.on('touchmove', this.onTouchMove, this);
30689             this.el.on('touchend', this.onTouchEnd, this);
30690             this.el.on('contextmenu', this.onContextMenu, this);
30691         } else {
30692             this.el.on('mouseenter'  ,this.enter, this);
30693             this.el.on('mouseleave', this.leave, this);
30694         }
30695         
30696         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30697             this.parent().bricks.push(this);   
30698         }
30699         
30700     },
30701     
30702     onClick: function(e, el)
30703     {
30704         if(!Roo.isTouch){
30705             return;
30706         }
30707         
30708         var time = this.endTimer - this.startTimer;
30709         
30710         //alert(time);
30711         
30712         if(time < 1000){
30713             return;
30714         }
30715         
30716         e.preventDefault();
30717     },
30718     
30719     enter: function(e, el)
30720     {
30721         e.preventDefault();
30722         
30723         if(this.bgimage.length && this.html.length){
30724             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30725         }
30726     },
30727     
30728     leave: function(e, el)
30729     {
30730         e.preventDefault();
30731         
30732         if(this.bgimage.length && this.html.length){
30733             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30734         }
30735     },
30736     
30737     onTouchStart: function(e, el)
30738     {
30739 //        e.preventDefault();
30740         
30741         this.touchmoved = false;
30742         
30743         if(!this.bgimage.length || !this.html.length){
30744             return;
30745         }
30746         
30747         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30748         
30749         this.timer = new Date().getTime();
30750         
30751     },
30752     
30753     onTouchMove: function(e, el)
30754     {
30755         this.touchmoved = true;
30756     },
30757     
30758     onContextMenu : function(e,el)
30759     {
30760         e.preventDefault();
30761         e.stopPropagation();
30762         return false;
30763     },
30764     
30765     onTouchEnd: function(e, el)
30766     {
30767 //        e.preventDefault();
30768         
30769         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30770         
30771             this.leave(e,el);
30772             
30773             return;
30774         }
30775         
30776         if(!this.bgimage.length || !this.html.length){
30777             
30778             if(this.href.length){
30779                 window.location.href = this.href;
30780             }
30781             
30782             return;
30783         }
30784         
30785         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30786         
30787         window.location.href = this.href;
30788     }
30789     
30790 });
30791
30792  
30793
30794  /*
30795  * - LGPL
30796  *
30797  * element
30798  * 
30799  */
30800
30801 /**
30802  * @class Roo.bootstrap.Brick
30803  * @extends Roo.bootstrap.Component
30804  * Bootstrap Brick class
30805  * 
30806  * @constructor
30807  * Create a new Brick
30808  * @param {Object} config The config object
30809  */
30810
30811 Roo.bootstrap.Brick = function(config){
30812     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30813     
30814     this.addEvents({
30815         // raw events
30816         /**
30817          * @event click
30818          * When a Brick is click
30819          * @param {Roo.bootstrap.Brick} this
30820          * @param {Roo.EventObject} e
30821          */
30822         "click" : true
30823     });
30824 };
30825
30826 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30827     
30828     /**
30829      * @cfg {String} title
30830      */   
30831     title : '',
30832     /**
30833      * @cfg {String} html
30834      */   
30835     html : '',
30836     /**
30837      * @cfg {String} bgimage
30838      */   
30839     bgimage : '',
30840     /**
30841      * @cfg {String} cls
30842      */   
30843     cls : '',
30844     /**
30845      * @cfg {String} href
30846      */   
30847     href : '',
30848     /**
30849      * @cfg {String} video
30850      */   
30851     video : '',
30852     /**
30853      * @cfg {Boolean} square
30854      */   
30855     square : true,
30856     
30857     getAutoCreate : function()
30858     {
30859         var cls = 'roo-brick';
30860         
30861         if(this.href.length){
30862             cls += ' roo-brick-link';
30863         }
30864         
30865         if(this.bgimage.length){
30866             cls += ' roo-brick-image';
30867         }
30868         
30869         if(!this.html.length && !this.bgimage.length){
30870             cls += ' roo-brick-center-title';
30871         }
30872         
30873         if(!this.html.length && this.bgimage.length){
30874             cls += ' roo-brick-bottom-title';
30875         }
30876         
30877         if(this.cls){
30878             cls += ' ' + this.cls;
30879         }
30880         
30881         var cfg = {
30882             tag: (this.href.length) ? 'a' : 'div',
30883             cls: cls,
30884             cn: [
30885                 {
30886                     tag: 'div',
30887                     cls: 'roo-brick-paragraph',
30888                     cn: []
30889                 }
30890             ]
30891         };
30892         
30893         if(this.href.length){
30894             cfg.href = this.href;
30895         }
30896         
30897         var cn = cfg.cn[0].cn;
30898         
30899         if(this.title.length){
30900             cn.push({
30901                 tag: 'h4',
30902                 cls: 'roo-brick-title',
30903                 html: this.title
30904             });
30905         }
30906         
30907         if(this.html.length){
30908             cn.push({
30909                 tag: 'p',
30910                 cls: 'roo-brick-text',
30911                 html: this.html
30912             });
30913         } else {
30914             cn.cls += ' hide';
30915         }
30916         
30917         if(this.bgimage.length){
30918             cfg.cn.push({
30919                 tag: 'img',
30920                 cls: 'roo-brick-image-view',
30921                 src: this.bgimage
30922             });
30923         }
30924         
30925         return cfg;
30926     },
30927     
30928     initEvents: function() 
30929     {
30930         if(this.title.length || this.html.length){
30931             this.el.on('mouseenter'  ,this.enter, this);
30932             this.el.on('mouseleave', this.leave, this);
30933         }
30934         
30935         
30936         Roo.EventManager.onWindowResize(this.resize, this); 
30937         
30938         this.resize();
30939     },
30940     
30941     resize : function()
30942     {
30943         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30944         
30945         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30946 //        paragraph.setHeight(paragraph.getWidth());
30947         
30948         if(this.bgimage.length){
30949             var image = this.el.select('.roo-brick-image-view', true).first();
30950             image.setWidth(paragraph.getWidth());
30951             image.setHeight(paragraph.getWidth());
30952         }
30953         
30954     },
30955     
30956     enter: function(e, el)
30957     {
30958         e.preventDefault();
30959         
30960         if(this.bgimage.length){
30961             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30962             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30963         }
30964     },
30965     
30966     leave: function(e, el)
30967     {
30968         e.preventDefault();
30969         
30970         if(this.bgimage.length){
30971             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30972             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30973         }
30974     }
30975     
30976 });
30977
30978  
30979
30980  /*
30981  * Based on:
30982  * Ext JS Library 1.1.1
30983  * Copyright(c) 2006-2007, Ext JS, LLC.
30984  *
30985  * Originally Released Under LGPL - original licence link has changed is not relivant.
30986  *
30987  * Fork - LGPL
30988  * <script type="text/javascript">
30989  */
30990
30991
30992 /**
30993  * @class Roo.bootstrap.SplitBar
30994  * @extends Roo.util.Observable
30995  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30996  * <br><br>
30997  * Usage:
30998  * <pre><code>
30999 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31000                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31001 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31002 split.minSize = 100;
31003 split.maxSize = 600;
31004 split.animate = true;
31005 split.on('moved', splitterMoved);
31006 </code></pre>
31007  * @constructor
31008  * Create a new SplitBar
31009  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31010  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31011  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31012  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31013                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31014                         position of the SplitBar).
31015  */
31016 Roo.bootstrap.SplitBar = function(cfg){
31017     
31018     /** @private */
31019     
31020     //{
31021     //  dragElement : elm
31022     //  resizingElement: el,
31023         // optional..
31024     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31025     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31026         // existingProxy ???
31027     //}
31028     
31029     this.el = Roo.get(cfg.dragElement, true);
31030     this.el.dom.unselectable = "on";
31031     /** @private */
31032     this.resizingEl = Roo.get(cfg.resizingElement, true);
31033
31034     /**
31035      * @private
31036      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31037      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31038      * @type Number
31039      */
31040     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31041     
31042     /**
31043      * The minimum size of the resizing element. (Defaults to 0)
31044      * @type Number
31045      */
31046     this.minSize = 0;
31047     
31048     /**
31049      * The maximum size of the resizing element. (Defaults to 2000)
31050      * @type Number
31051      */
31052     this.maxSize = 2000;
31053     
31054     /**
31055      * Whether to animate the transition to the new size
31056      * @type Boolean
31057      */
31058     this.animate = false;
31059     
31060     /**
31061      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31062      * @type Boolean
31063      */
31064     this.useShim = false;
31065     
31066     /** @private */
31067     this.shim = null;
31068     
31069     if(!cfg.existingProxy){
31070         /** @private */
31071         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31072     }else{
31073         this.proxy = Roo.get(cfg.existingProxy).dom;
31074     }
31075     /** @private */
31076     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31077     
31078     /** @private */
31079     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31080     
31081     /** @private */
31082     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31083     
31084     /** @private */
31085     this.dragSpecs = {};
31086     
31087     /**
31088      * @private The adapter to use to positon and resize elements
31089      */
31090     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31091     this.adapter.init(this);
31092     
31093     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31094         /** @private */
31095         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31096         this.el.addClass("roo-splitbar-h");
31097     }else{
31098         /** @private */
31099         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31100         this.el.addClass("roo-splitbar-v");
31101     }
31102     
31103     this.addEvents({
31104         /**
31105          * @event resize
31106          * Fires when the splitter is moved (alias for {@link #event-moved})
31107          * @param {Roo.bootstrap.SplitBar} this
31108          * @param {Number} newSize the new width or height
31109          */
31110         "resize" : true,
31111         /**
31112          * @event moved
31113          * Fires when the splitter is moved
31114          * @param {Roo.bootstrap.SplitBar} this
31115          * @param {Number} newSize the new width or height
31116          */
31117         "moved" : true,
31118         /**
31119          * @event beforeresize
31120          * Fires before the splitter is dragged
31121          * @param {Roo.bootstrap.SplitBar} this
31122          */
31123         "beforeresize" : true,
31124
31125         "beforeapply" : true
31126     });
31127
31128     Roo.util.Observable.call(this);
31129 };
31130
31131 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31132     onStartProxyDrag : function(x, y){
31133         this.fireEvent("beforeresize", this);
31134         if(!this.overlay){
31135             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31136             o.unselectable();
31137             o.enableDisplayMode("block");
31138             // all splitbars share the same overlay
31139             Roo.bootstrap.SplitBar.prototype.overlay = o;
31140         }
31141         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31142         this.overlay.show();
31143         Roo.get(this.proxy).setDisplayed("block");
31144         var size = this.adapter.getElementSize(this);
31145         this.activeMinSize = this.getMinimumSize();;
31146         this.activeMaxSize = this.getMaximumSize();;
31147         var c1 = size - this.activeMinSize;
31148         var c2 = Math.max(this.activeMaxSize - size, 0);
31149         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31150             this.dd.resetConstraints();
31151             this.dd.setXConstraint(
31152                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31153                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31154             );
31155             this.dd.setYConstraint(0, 0);
31156         }else{
31157             this.dd.resetConstraints();
31158             this.dd.setXConstraint(0, 0);
31159             this.dd.setYConstraint(
31160                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31161                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31162             );
31163          }
31164         this.dragSpecs.startSize = size;
31165         this.dragSpecs.startPoint = [x, y];
31166         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31167     },
31168     
31169     /** 
31170      * @private Called after the drag operation by the DDProxy
31171      */
31172     onEndProxyDrag : function(e){
31173         Roo.get(this.proxy).setDisplayed(false);
31174         var endPoint = Roo.lib.Event.getXY(e);
31175         if(this.overlay){
31176             this.overlay.hide();
31177         }
31178         var newSize;
31179         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31180             newSize = this.dragSpecs.startSize + 
31181                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31182                     endPoint[0] - this.dragSpecs.startPoint[0] :
31183                     this.dragSpecs.startPoint[0] - endPoint[0]
31184                 );
31185         }else{
31186             newSize = this.dragSpecs.startSize + 
31187                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31188                     endPoint[1] - this.dragSpecs.startPoint[1] :
31189                     this.dragSpecs.startPoint[1] - endPoint[1]
31190                 );
31191         }
31192         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31193         if(newSize != this.dragSpecs.startSize){
31194             if(this.fireEvent('beforeapply', this, newSize) !== false){
31195                 this.adapter.setElementSize(this, newSize);
31196                 this.fireEvent("moved", this, newSize);
31197                 this.fireEvent("resize", this, newSize);
31198             }
31199         }
31200     },
31201     
31202     /**
31203      * Get the adapter this SplitBar uses
31204      * @return The adapter object
31205      */
31206     getAdapter : function(){
31207         return this.adapter;
31208     },
31209     
31210     /**
31211      * Set the adapter this SplitBar uses
31212      * @param {Object} adapter A SplitBar adapter object
31213      */
31214     setAdapter : function(adapter){
31215         this.adapter = adapter;
31216         this.adapter.init(this);
31217     },
31218     
31219     /**
31220      * Gets the minimum size for the resizing element
31221      * @return {Number} The minimum size
31222      */
31223     getMinimumSize : function(){
31224         return this.minSize;
31225     },
31226     
31227     /**
31228      * Sets the minimum size for the resizing element
31229      * @param {Number} minSize The minimum size
31230      */
31231     setMinimumSize : function(minSize){
31232         this.minSize = minSize;
31233     },
31234     
31235     /**
31236      * Gets the maximum size for the resizing element
31237      * @return {Number} The maximum size
31238      */
31239     getMaximumSize : function(){
31240         return this.maxSize;
31241     },
31242     
31243     /**
31244      * Sets the maximum size for the resizing element
31245      * @param {Number} maxSize The maximum size
31246      */
31247     setMaximumSize : function(maxSize){
31248         this.maxSize = maxSize;
31249     },
31250     
31251     /**
31252      * Sets the initialize size for the resizing element
31253      * @param {Number} size The initial size
31254      */
31255     setCurrentSize : function(size){
31256         var oldAnimate = this.animate;
31257         this.animate = false;
31258         this.adapter.setElementSize(this, size);
31259         this.animate = oldAnimate;
31260     },
31261     
31262     /**
31263      * Destroy this splitbar. 
31264      * @param {Boolean} removeEl True to remove the element
31265      */
31266     destroy : function(removeEl){
31267         if(this.shim){
31268             this.shim.remove();
31269         }
31270         this.dd.unreg();
31271         this.proxy.parentNode.removeChild(this.proxy);
31272         if(removeEl){
31273             this.el.remove();
31274         }
31275     }
31276 });
31277
31278 /**
31279  * @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.
31280  */
31281 Roo.bootstrap.SplitBar.createProxy = function(dir){
31282     var proxy = new Roo.Element(document.createElement("div"));
31283     proxy.unselectable();
31284     var cls = 'roo-splitbar-proxy';
31285     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31286     document.body.appendChild(proxy.dom);
31287     return proxy.dom;
31288 };
31289
31290 /** 
31291  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31292  * Default Adapter. It assumes the splitter and resizing element are not positioned
31293  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31294  */
31295 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31296 };
31297
31298 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31299     // do nothing for now
31300     init : function(s){
31301     
31302     },
31303     /**
31304      * Called before drag operations to get the current size of the resizing element. 
31305      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31306      */
31307      getElementSize : function(s){
31308         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31309             return s.resizingEl.getWidth();
31310         }else{
31311             return s.resizingEl.getHeight();
31312         }
31313     },
31314     
31315     /**
31316      * Called after drag operations to set the size of the resizing element.
31317      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31318      * @param {Number} newSize The new size to set
31319      * @param {Function} onComplete A function to be invoked when resizing is complete
31320      */
31321     setElementSize : function(s, newSize, onComplete){
31322         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31323             if(!s.animate){
31324                 s.resizingEl.setWidth(newSize);
31325                 if(onComplete){
31326                     onComplete(s, newSize);
31327                 }
31328             }else{
31329                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31330             }
31331         }else{
31332             
31333             if(!s.animate){
31334                 s.resizingEl.setHeight(newSize);
31335                 if(onComplete){
31336                     onComplete(s, newSize);
31337                 }
31338             }else{
31339                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31340             }
31341         }
31342     }
31343 };
31344
31345 /** 
31346  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31347  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31348  * Adapter that  moves the splitter element to align with the resized sizing element. 
31349  * Used with an absolute positioned SplitBar.
31350  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31351  * document.body, make sure you assign an id to the body element.
31352  */
31353 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31354     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31355     this.container = Roo.get(container);
31356 };
31357
31358 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31359     init : function(s){
31360         this.basic.init(s);
31361     },
31362     
31363     getElementSize : function(s){
31364         return this.basic.getElementSize(s);
31365     },
31366     
31367     setElementSize : function(s, newSize, onComplete){
31368         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31369     },
31370     
31371     moveSplitter : function(s){
31372         var yes = Roo.bootstrap.SplitBar;
31373         switch(s.placement){
31374             case yes.LEFT:
31375                 s.el.setX(s.resizingEl.getRight());
31376                 break;
31377             case yes.RIGHT:
31378                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31379                 break;
31380             case yes.TOP:
31381                 s.el.setY(s.resizingEl.getBottom());
31382                 break;
31383             case yes.BOTTOM:
31384                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31385                 break;
31386         }
31387     }
31388 };
31389
31390 /**
31391  * Orientation constant - Create a vertical SplitBar
31392  * @static
31393  * @type Number
31394  */
31395 Roo.bootstrap.SplitBar.VERTICAL = 1;
31396
31397 /**
31398  * Orientation constant - Create a horizontal SplitBar
31399  * @static
31400  * @type Number
31401  */
31402 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31403
31404 /**
31405  * Placement constant - The resizing element is to the left of the splitter element
31406  * @static
31407  * @type Number
31408  */
31409 Roo.bootstrap.SplitBar.LEFT = 1;
31410
31411 /**
31412  * Placement constant - The resizing element is to the right of the splitter element
31413  * @static
31414  * @type Number
31415  */
31416 Roo.bootstrap.SplitBar.RIGHT = 2;
31417
31418 /**
31419  * Placement constant - The resizing element is positioned above the splitter element
31420  * @static
31421  * @type Number
31422  */
31423 Roo.bootstrap.SplitBar.TOP = 3;
31424
31425 /**
31426  * Placement constant - The resizing element is positioned under splitter element
31427  * @static
31428  * @type Number
31429  */
31430 Roo.bootstrap.SplitBar.BOTTOM = 4;
31431 Roo.namespace("Roo.bootstrap.layout");/*
31432  * Based on:
31433  * Ext JS Library 1.1.1
31434  * Copyright(c) 2006-2007, Ext JS, LLC.
31435  *
31436  * Originally Released Under LGPL - original licence link has changed is not relivant.
31437  *
31438  * Fork - LGPL
31439  * <script type="text/javascript">
31440  */
31441  
31442 /**
31443  * @class Roo.bootstrap.layout.Manager
31444  * @extends Roo.bootstrap.Component
31445  * Base class for layout managers.
31446  */
31447 Roo.bootstrap.layout.Manager = function(config)
31448 {
31449     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31450     
31451     
31452      
31453     
31454     
31455     /** false to disable window resize monitoring @type Boolean */
31456     this.monitorWindowResize = true;
31457     this.regions = {};
31458     this.addEvents({
31459         /**
31460          * @event layout
31461          * Fires when a layout is performed. 
31462          * @param {Roo.LayoutManager} this
31463          */
31464         "layout" : true,
31465         /**
31466          * @event regionresized
31467          * Fires when the user resizes a region. 
31468          * @param {Roo.LayoutRegion} region The resized region
31469          * @param {Number} newSize The new size (width for east/west, height for north/south)
31470          */
31471         "regionresized" : true,
31472         /**
31473          * @event regioncollapsed
31474          * Fires when a region is collapsed. 
31475          * @param {Roo.LayoutRegion} region The collapsed region
31476          */
31477         "regioncollapsed" : true,
31478         /**
31479          * @event regionexpanded
31480          * Fires when a region is expanded.  
31481          * @param {Roo.LayoutRegion} region The expanded region
31482          */
31483         "regionexpanded" : true
31484     });
31485     this.updating = false;
31486     
31487     if (config.el) {
31488         this.el = Roo.get(config.el);
31489         this.initEvents();
31490     }
31491     
31492 };
31493
31494 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31495     
31496     
31497     regions : null,
31498     
31499     monitorWindowResize : true,
31500     
31501     
31502     updating : false,
31503     
31504     
31505     onRender : function(ct, position)
31506     {
31507         if(!this.el){
31508             this.el = Roo.get(ct);
31509             this.initEvents();
31510         }
31511     },
31512     
31513     
31514     initEvents: function()
31515     {
31516         
31517         
31518         // ie scrollbar fix
31519         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31520             document.body.scroll = "no";
31521         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31522             this.el.position('relative');
31523         }
31524         this.id = this.el.id;
31525         this.el.addClass("roo-layout-container");
31526         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31527         if(this.el.dom != document.body ) {
31528             this.el.on('resize', this.layout,this);
31529             this.el.on('show', this.layout,this);
31530         }
31531
31532     },
31533     
31534     /**
31535      * Returns true if this layout is currently being updated
31536      * @return {Boolean}
31537      */
31538     isUpdating : function(){
31539         return this.updating; 
31540     },
31541     
31542     /**
31543      * Suspend the LayoutManager from doing auto-layouts while
31544      * making multiple add or remove calls
31545      */
31546     beginUpdate : function(){
31547         this.updating = true;    
31548     },
31549     
31550     /**
31551      * Restore auto-layouts and optionally disable the manager from performing a layout
31552      * @param {Boolean} noLayout true to disable a layout update 
31553      */
31554     endUpdate : function(noLayout){
31555         this.updating = false;
31556         if(!noLayout){
31557             this.layout();
31558         }    
31559     },
31560     
31561     layout: function(){
31562         // abstract...
31563     },
31564     
31565     onRegionResized : function(region, newSize){
31566         this.fireEvent("regionresized", region, newSize);
31567         this.layout();
31568     },
31569     
31570     onRegionCollapsed : function(region){
31571         this.fireEvent("regioncollapsed", region);
31572     },
31573     
31574     onRegionExpanded : function(region){
31575         this.fireEvent("regionexpanded", region);
31576     },
31577         
31578     /**
31579      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31580      * performs box-model adjustments.
31581      * @return {Object} The size as an object {width: (the width), height: (the height)}
31582      */
31583     getViewSize : function()
31584     {
31585         var size;
31586         if(this.el.dom != document.body){
31587             size = this.el.getSize();
31588         }else{
31589             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31590         }
31591         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31592         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31593         return size;
31594     },
31595     
31596     /**
31597      * Returns the Element this layout is bound to.
31598      * @return {Roo.Element}
31599      */
31600     getEl : function(){
31601         return this.el;
31602     },
31603     
31604     /**
31605      * Returns the specified region.
31606      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31607      * @return {Roo.LayoutRegion}
31608      */
31609     getRegion : function(target){
31610         return this.regions[target.toLowerCase()];
31611     },
31612     
31613     onWindowResize : function(){
31614         if(this.monitorWindowResize){
31615             this.layout();
31616         }
31617     }
31618 });/*
31619  * Based on:
31620  * Ext JS Library 1.1.1
31621  * Copyright(c) 2006-2007, Ext JS, LLC.
31622  *
31623  * Originally Released Under LGPL - original licence link has changed is not relivant.
31624  *
31625  * Fork - LGPL
31626  * <script type="text/javascript">
31627  */
31628 /**
31629  * @class Roo.bootstrap.layout.Border
31630  * @extends Roo.bootstrap.layout.Manager
31631  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31632  * please see: examples/bootstrap/nested.html<br><br>
31633  
31634 <b>The container the layout is rendered into can be either the body element or any other element.
31635 If it is not the body element, the container needs to either be an absolute positioned element,
31636 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31637 the container size if it is not the body element.</b>
31638
31639 * @constructor
31640 * Create a new Border
31641 * @param {Object} config Configuration options
31642  */
31643 Roo.bootstrap.layout.Border = function(config){
31644     config = config || {};
31645     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31646     
31647     
31648     
31649     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31650         if(config[region]){
31651             config[region].region = region;
31652             this.addRegion(config[region]);
31653         }
31654     },this);
31655     
31656 };
31657
31658 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31659
31660 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31661     /**
31662      * Creates and adds a new region if it doesn't already exist.
31663      * @param {String} target The target region key (north, south, east, west or center).
31664      * @param {Object} config The regions config object
31665      * @return {BorderLayoutRegion} The new region
31666      */
31667     addRegion : function(config)
31668     {
31669         if(!this.regions[config.region]){
31670             var r = this.factory(config);
31671             this.bindRegion(r);
31672         }
31673         return this.regions[config.region];
31674     },
31675
31676     // private (kinda)
31677     bindRegion : function(r){
31678         this.regions[r.config.region] = r;
31679         
31680         r.on("visibilitychange",    this.layout, this);
31681         r.on("paneladded",          this.layout, this);
31682         r.on("panelremoved",        this.layout, this);
31683         r.on("invalidated",         this.layout, this);
31684         r.on("resized",             this.onRegionResized, this);
31685         r.on("collapsed",           this.onRegionCollapsed, this);
31686         r.on("expanded",            this.onRegionExpanded, this);
31687     },
31688
31689     /**
31690      * Performs a layout update.
31691      */
31692     layout : function()
31693     {
31694         if(this.updating) {
31695             return;
31696         }
31697         var size = this.getViewSize();
31698         var w = size.width;
31699         var h = size.height;
31700         var centerW = w;
31701         var centerH = h;
31702         var centerY = 0;
31703         var centerX = 0;
31704         //var x = 0, y = 0;
31705
31706         var rs = this.regions;
31707         var north = rs["north"];
31708         var south = rs["south"]; 
31709         var west = rs["west"];
31710         var east = rs["east"];
31711         var center = rs["center"];
31712         //if(this.hideOnLayout){ // not supported anymore
31713             //c.el.setStyle("display", "none");
31714         //}
31715         if(north && north.isVisible()){
31716             var b = north.getBox();
31717             var m = north.getMargins();
31718             b.width = w - (m.left+m.right);
31719             b.x = m.left;
31720             b.y = m.top;
31721             centerY = b.height + b.y + m.bottom;
31722             centerH -= centerY;
31723             north.updateBox(this.safeBox(b));
31724         }
31725         if(south && south.isVisible()){
31726             var b = south.getBox();
31727             var m = south.getMargins();
31728             b.width = w - (m.left+m.right);
31729             b.x = m.left;
31730             var totalHeight = (b.height + m.top + m.bottom);
31731             b.y = h - totalHeight + m.top;
31732             centerH -= totalHeight;
31733             south.updateBox(this.safeBox(b));
31734         }
31735         if(west && west.isVisible()){
31736             var b = west.getBox();
31737             var m = west.getMargins();
31738             b.height = centerH - (m.top+m.bottom);
31739             b.x = m.left;
31740             b.y = centerY + m.top;
31741             var totalWidth = (b.width + m.left + m.right);
31742             centerX += totalWidth;
31743             centerW -= totalWidth;
31744             west.updateBox(this.safeBox(b));
31745         }
31746         if(east && east.isVisible()){
31747             var b = east.getBox();
31748             var m = east.getMargins();
31749             b.height = centerH - (m.top+m.bottom);
31750             var totalWidth = (b.width + m.left + m.right);
31751             b.x = w - totalWidth + m.left;
31752             b.y = centerY + m.top;
31753             centerW -= totalWidth;
31754             east.updateBox(this.safeBox(b));
31755         }
31756         if(center){
31757             var m = center.getMargins();
31758             var centerBox = {
31759                 x: centerX + m.left,
31760                 y: centerY + m.top,
31761                 width: centerW - (m.left+m.right),
31762                 height: centerH - (m.top+m.bottom)
31763             };
31764             //if(this.hideOnLayout){
31765                 //center.el.setStyle("display", "block");
31766             //}
31767             center.updateBox(this.safeBox(centerBox));
31768         }
31769         this.el.repaint();
31770         this.fireEvent("layout", this);
31771     },
31772
31773     // private
31774     safeBox : function(box){
31775         box.width = Math.max(0, box.width);
31776         box.height = Math.max(0, box.height);
31777         return box;
31778     },
31779
31780     /**
31781      * Adds a ContentPanel (or subclass) to this layout.
31782      * @param {String} target The target region key (north, south, east, west or center).
31783      * @param {Roo.ContentPanel} panel The panel to add
31784      * @return {Roo.ContentPanel} The added panel
31785      */
31786     add : function(target, panel){
31787          
31788         target = target.toLowerCase();
31789         return this.regions[target].add(panel);
31790     },
31791
31792     /**
31793      * Remove a ContentPanel (or subclass) to this layout.
31794      * @param {String} target The target region key (north, south, east, west or center).
31795      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31796      * @return {Roo.ContentPanel} The removed panel
31797      */
31798     remove : function(target, panel){
31799         target = target.toLowerCase();
31800         return this.regions[target].remove(panel);
31801     },
31802
31803     /**
31804      * Searches all regions for a panel with the specified id
31805      * @param {String} panelId
31806      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31807      */
31808     findPanel : function(panelId){
31809         var rs = this.regions;
31810         for(var target in rs){
31811             if(typeof rs[target] != "function"){
31812                 var p = rs[target].getPanel(panelId);
31813                 if(p){
31814                     return p;
31815                 }
31816             }
31817         }
31818         return null;
31819     },
31820
31821     /**
31822      * Searches all regions for a panel with the specified id and activates (shows) it.
31823      * @param {String/ContentPanel} panelId The panels id or the panel itself
31824      * @return {Roo.ContentPanel} The shown panel or null
31825      */
31826     showPanel : function(panelId) {
31827       var rs = this.regions;
31828       for(var target in rs){
31829          var r = rs[target];
31830          if(typeof r != "function"){
31831             if(r.hasPanel(panelId)){
31832                return r.showPanel(panelId);
31833             }
31834          }
31835       }
31836       return null;
31837    },
31838
31839    /**
31840      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31841      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31842      */
31843    /*
31844     restoreState : function(provider){
31845         if(!provider){
31846             provider = Roo.state.Manager;
31847         }
31848         var sm = new Roo.LayoutStateManager();
31849         sm.init(this, provider);
31850     },
31851 */
31852  
31853  
31854     /**
31855      * Adds a xtype elements to the layout.
31856      * <pre><code>
31857
31858 layout.addxtype({
31859        xtype : 'ContentPanel',
31860        region: 'west',
31861        items: [ .... ]
31862    }
31863 );
31864
31865 layout.addxtype({
31866         xtype : 'NestedLayoutPanel',
31867         region: 'west',
31868         layout: {
31869            center: { },
31870            west: { }   
31871         },
31872         items : [ ... list of content panels or nested layout panels.. ]
31873    }
31874 );
31875 </code></pre>
31876      * @param {Object} cfg Xtype definition of item to add.
31877      */
31878     addxtype : function(cfg)
31879     {
31880         // basically accepts a pannel...
31881         // can accept a layout region..!?!?
31882         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31883         
31884         
31885         // theory?  children can only be panels??
31886         
31887         //if (!cfg.xtype.match(/Panel$/)) {
31888         //    return false;
31889         //}
31890         var ret = false;
31891         
31892         if (typeof(cfg.region) == 'undefined') {
31893             Roo.log("Failed to add Panel, region was not set");
31894             Roo.log(cfg);
31895             return false;
31896         }
31897         var region = cfg.region;
31898         delete cfg.region;
31899         
31900           
31901         var xitems = [];
31902         if (cfg.items) {
31903             xitems = cfg.items;
31904             delete cfg.items;
31905         }
31906         var nb = false;
31907         
31908         switch(cfg.xtype) 
31909         {
31910             case 'Content':  // ContentPanel (el, cfg)
31911             case 'Scroll':  // ContentPanel (el, cfg)
31912             case 'View': 
31913                 cfg.autoCreate = true;
31914                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31915                 //} else {
31916                 //    var el = this.el.createChild();
31917                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31918                 //}
31919                 
31920                 this.add(region, ret);
31921                 break;
31922             
31923             /*
31924             case 'TreePanel': // our new panel!
31925                 cfg.el = this.el.createChild();
31926                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31927                 this.add(region, ret);
31928                 break;
31929             */
31930             
31931             case 'Nest': 
31932                 // create a new Layout (which is  a Border Layout...
31933                 
31934                 var clayout = cfg.layout;
31935                 clayout.el  = this.el.createChild();
31936                 clayout.items   = clayout.items  || [];
31937                 
31938                 delete cfg.layout;
31939                 
31940                 // replace this exitems with the clayout ones..
31941                 xitems = clayout.items;
31942                  
31943                 // force background off if it's in center...
31944                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31945                     cfg.background = false;
31946                 }
31947                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31948                 
31949                 
31950                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31951                 //console.log('adding nested layout panel '  + cfg.toSource());
31952                 this.add(region, ret);
31953                 nb = {}; /// find first...
31954                 break;
31955             
31956             case 'Grid':
31957                 
31958                 // needs grid and region
31959                 
31960                 //var el = this.getRegion(region).el.createChild();
31961                 /*
31962                  *var el = this.el.createChild();
31963                 // create the grid first...
31964                 cfg.grid.container = el;
31965                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31966                 */
31967                 
31968                 if (region == 'center' && this.active ) {
31969                     cfg.background = false;
31970                 }
31971                 
31972                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31973                 
31974                 this.add(region, ret);
31975                 /*
31976                 if (cfg.background) {
31977                     // render grid on panel activation (if panel background)
31978                     ret.on('activate', function(gp) {
31979                         if (!gp.grid.rendered) {
31980                     //        gp.grid.render(el);
31981                         }
31982                     });
31983                 } else {
31984                   //  cfg.grid.render(el);
31985                 }
31986                 */
31987                 break;
31988            
31989            
31990             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31991                 // it was the old xcomponent building that caused this before.
31992                 // espeically if border is the top element in the tree.
31993                 ret = this;
31994                 break; 
31995                 
31996                     
31997                 
31998                 
31999                 
32000             default:
32001                 /*
32002                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32003                     
32004                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32005                     this.add(region, ret);
32006                 } else {
32007                 */
32008                     Roo.log(cfg);
32009                     throw "Can not add '" + cfg.xtype + "' to Border";
32010                     return null;
32011              
32012                                 
32013              
32014         }
32015         this.beginUpdate();
32016         // add children..
32017         var region = '';
32018         var abn = {};
32019         Roo.each(xitems, function(i)  {
32020             region = nb && i.region ? i.region : false;
32021             
32022             var add = ret.addxtype(i);
32023            
32024             if (region) {
32025                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32026                 if (!i.background) {
32027                     abn[region] = nb[region] ;
32028                 }
32029             }
32030             
32031         });
32032         this.endUpdate();
32033
32034         // make the last non-background panel active..
32035         //if (nb) { Roo.log(abn); }
32036         if (nb) {
32037             
32038             for(var r in abn) {
32039                 region = this.getRegion(r);
32040                 if (region) {
32041                     // tried using nb[r], but it does not work..
32042                      
32043                     region.showPanel(abn[r]);
32044                    
32045                 }
32046             }
32047         }
32048         return ret;
32049         
32050     },
32051     
32052     
32053 // private
32054     factory : function(cfg)
32055     {
32056         
32057         var validRegions = Roo.bootstrap.layout.Border.regions;
32058
32059         var target = cfg.region;
32060         cfg.mgr = this;
32061         
32062         var r = Roo.bootstrap.layout;
32063         Roo.log(target);
32064         switch(target){
32065             case "north":
32066                 return new r.North(cfg);
32067             case "south":
32068                 return new r.South(cfg);
32069             case "east":
32070                 return new r.East(cfg);
32071             case "west":
32072                 return new r.West(cfg);
32073             case "center":
32074                 return new r.Center(cfg);
32075         }
32076         throw 'Layout region "'+target+'" not supported.';
32077     }
32078     
32079     
32080 });
32081  /*
32082  * Based on:
32083  * Ext JS Library 1.1.1
32084  * Copyright(c) 2006-2007, Ext JS, LLC.
32085  *
32086  * Originally Released Under LGPL - original licence link has changed is not relivant.
32087  *
32088  * Fork - LGPL
32089  * <script type="text/javascript">
32090  */
32091  
32092 /**
32093  * @class Roo.bootstrap.layout.Basic
32094  * @extends Roo.util.Observable
32095  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32096  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32097  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32098  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32099  * @cfg {string}   region  the region that it inhabits..
32100  * @cfg {bool}   skipConfig skip config?
32101  * 
32102
32103  */
32104 Roo.bootstrap.layout.Basic = function(config){
32105     
32106     this.mgr = config.mgr;
32107     
32108     this.position = config.region;
32109     
32110     var skipConfig = config.skipConfig;
32111     
32112     this.events = {
32113         /**
32114          * @scope Roo.BasicLayoutRegion
32115          */
32116         
32117         /**
32118          * @event beforeremove
32119          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32120          * @param {Roo.LayoutRegion} this
32121          * @param {Roo.ContentPanel} panel The panel
32122          * @param {Object} e The cancel event object
32123          */
32124         "beforeremove" : true,
32125         /**
32126          * @event invalidated
32127          * Fires when the layout for this region is changed.
32128          * @param {Roo.LayoutRegion} this
32129          */
32130         "invalidated" : true,
32131         /**
32132          * @event visibilitychange
32133          * Fires when this region is shown or hidden 
32134          * @param {Roo.LayoutRegion} this
32135          * @param {Boolean} visibility true or false
32136          */
32137         "visibilitychange" : true,
32138         /**
32139          * @event paneladded
32140          * Fires when a panel is added. 
32141          * @param {Roo.LayoutRegion} this
32142          * @param {Roo.ContentPanel} panel The panel
32143          */
32144         "paneladded" : true,
32145         /**
32146          * @event panelremoved
32147          * Fires when a panel is removed. 
32148          * @param {Roo.LayoutRegion} this
32149          * @param {Roo.ContentPanel} panel The panel
32150          */
32151         "panelremoved" : true,
32152         /**
32153          * @event beforecollapse
32154          * Fires when this region before collapse.
32155          * @param {Roo.LayoutRegion} this
32156          */
32157         "beforecollapse" : true,
32158         /**
32159          * @event collapsed
32160          * Fires when this region is collapsed.
32161          * @param {Roo.LayoutRegion} this
32162          */
32163         "collapsed" : true,
32164         /**
32165          * @event expanded
32166          * Fires when this region is expanded.
32167          * @param {Roo.LayoutRegion} this
32168          */
32169         "expanded" : true,
32170         /**
32171          * @event slideshow
32172          * Fires when this region is slid into view.
32173          * @param {Roo.LayoutRegion} this
32174          */
32175         "slideshow" : true,
32176         /**
32177          * @event slidehide
32178          * Fires when this region slides out of view. 
32179          * @param {Roo.LayoutRegion} this
32180          */
32181         "slidehide" : true,
32182         /**
32183          * @event panelactivated
32184          * Fires when a panel is activated. 
32185          * @param {Roo.LayoutRegion} this
32186          * @param {Roo.ContentPanel} panel The activated panel
32187          */
32188         "panelactivated" : true,
32189         /**
32190          * @event resized
32191          * Fires when the user resizes this region. 
32192          * @param {Roo.LayoutRegion} this
32193          * @param {Number} newSize The new size (width for east/west, height for north/south)
32194          */
32195         "resized" : true
32196     };
32197     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32198     this.panels = new Roo.util.MixedCollection();
32199     this.panels.getKey = this.getPanelId.createDelegate(this);
32200     this.box = null;
32201     this.activePanel = null;
32202     // ensure listeners are added...
32203     
32204     if (config.listeners || config.events) {
32205         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32206             listeners : config.listeners || {},
32207             events : config.events || {}
32208         });
32209     }
32210     
32211     if(skipConfig !== true){
32212         this.applyConfig(config);
32213     }
32214 };
32215
32216 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32217 {
32218     getPanelId : function(p){
32219         return p.getId();
32220     },
32221     
32222     applyConfig : function(config){
32223         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32224         this.config = config;
32225         
32226     },
32227     
32228     /**
32229      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32230      * the width, for horizontal (north, south) the height.
32231      * @param {Number} newSize The new width or height
32232      */
32233     resizeTo : function(newSize){
32234         var el = this.el ? this.el :
32235                  (this.activePanel ? this.activePanel.getEl() : null);
32236         if(el){
32237             switch(this.position){
32238                 case "east":
32239                 case "west":
32240                     el.setWidth(newSize);
32241                     this.fireEvent("resized", this, newSize);
32242                 break;
32243                 case "north":
32244                 case "south":
32245                     el.setHeight(newSize);
32246                     this.fireEvent("resized", this, newSize);
32247                 break;                
32248             }
32249         }
32250     },
32251     
32252     getBox : function(){
32253         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32254     },
32255     
32256     getMargins : function(){
32257         return this.margins;
32258     },
32259     
32260     updateBox : function(box){
32261         this.box = box;
32262         var el = this.activePanel.getEl();
32263         el.dom.style.left = box.x + "px";
32264         el.dom.style.top = box.y + "px";
32265         this.activePanel.setSize(box.width, box.height);
32266     },
32267     
32268     /**
32269      * Returns the container element for this region.
32270      * @return {Roo.Element}
32271      */
32272     getEl : function(){
32273         return this.activePanel;
32274     },
32275     
32276     /**
32277      * Returns true if this region is currently visible.
32278      * @return {Boolean}
32279      */
32280     isVisible : function(){
32281         return this.activePanel ? true : false;
32282     },
32283     
32284     setActivePanel : function(panel){
32285         panel = this.getPanel(panel);
32286         if(this.activePanel && this.activePanel != panel){
32287             this.activePanel.setActiveState(false);
32288             this.activePanel.getEl().setLeftTop(-10000,-10000);
32289         }
32290         this.activePanel = panel;
32291         panel.setActiveState(true);
32292         if(this.box){
32293             panel.setSize(this.box.width, this.box.height);
32294         }
32295         this.fireEvent("panelactivated", this, panel);
32296         this.fireEvent("invalidated");
32297     },
32298     
32299     /**
32300      * Show the specified panel.
32301      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32302      * @return {Roo.ContentPanel} The shown panel or null
32303      */
32304     showPanel : function(panel){
32305         panel = this.getPanel(panel);
32306         if(panel){
32307             this.setActivePanel(panel);
32308         }
32309         return panel;
32310     },
32311     
32312     /**
32313      * Get the active panel for this region.
32314      * @return {Roo.ContentPanel} The active panel or null
32315      */
32316     getActivePanel : function(){
32317         return this.activePanel;
32318     },
32319     
32320     /**
32321      * Add the passed ContentPanel(s)
32322      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32323      * @return {Roo.ContentPanel} The panel added (if only one was added)
32324      */
32325     add : function(panel){
32326         if(arguments.length > 1){
32327             for(var i = 0, len = arguments.length; i < len; i++) {
32328                 this.add(arguments[i]);
32329             }
32330             return null;
32331         }
32332         if(this.hasPanel(panel)){
32333             this.showPanel(panel);
32334             return panel;
32335         }
32336         var el = panel.getEl();
32337         if(el.dom.parentNode != this.mgr.el.dom){
32338             this.mgr.el.dom.appendChild(el.dom);
32339         }
32340         if(panel.setRegion){
32341             panel.setRegion(this);
32342         }
32343         this.panels.add(panel);
32344         el.setStyle("position", "absolute");
32345         if(!panel.background){
32346             this.setActivePanel(panel);
32347             if(this.config.initialSize && this.panels.getCount()==1){
32348                 this.resizeTo(this.config.initialSize);
32349             }
32350         }
32351         this.fireEvent("paneladded", this, panel);
32352         return panel;
32353     },
32354     
32355     /**
32356      * Returns true if the panel is in this region.
32357      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32358      * @return {Boolean}
32359      */
32360     hasPanel : function(panel){
32361         if(typeof panel == "object"){ // must be panel obj
32362             panel = panel.getId();
32363         }
32364         return this.getPanel(panel) ? true : false;
32365     },
32366     
32367     /**
32368      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32369      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32370      * @param {Boolean} preservePanel Overrides the config preservePanel option
32371      * @return {Roo.ContentPanel} The panel that was removed
32372      */
32373     remove : function(panel, preservePanel){
32374         panel = this.getPanel(panel);
32375         if(!panel){
32376             return null;
32377         }
32378         var e = {};
32379         this.fireEvent("beforeremove", this, panel, e);
32380         if(e.cancel === true){
32381             return null;
32382         }
32383         var panelId = panel.getId();
32384         this.panels.removeKey(panelId);
32385         return panel;
32386     },
32387     
32388     /**
32389      * Returns the panel specified or null if it's not in this region.
32390      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32391      * @return {Roo.ContentPanel}
32392      */
32393     getPanel : function(id){
32394         if(typeof id == "object"){ // must be panel obj
32395             return id;
32396         }
32397         return this.panels.get(id);
32398     },
32399     
32400     /**
32401      * Returns this regions position (north/south/east/west/center).
32402      * @return {String} 
32403      */
32404     getPosition: function(){
32405         return this.position;    
32406     }
32407 });/*
32408  * Based on:
32409  * Ext JS Library 1.1.1
32410  * Copyright(c) 2006-2007, Ext JS, LLC.
32411  *
32412  * Originally Released Under LGPL - original licence link has changed is not relivant.
32413  *
32414  * Fork - LGPL
32415  * <script type="text/javascript">
32416  */
32417  
32418 /**
32419  * @class Roo.bootstrap.layout.Region
32420  * @extends Roo.bootstrap.layout.Basic
32421  * This class represents a region in a layout manager.
32422  
32423  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32424  * @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})
32425  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32426  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32427  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32428  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32429  * @cfg {String}    title           The title for the region (overrides panel titles)
32430  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32431  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32432  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32433  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32434  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32435  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32436  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32437  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32438  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32439  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32440
32441  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32442  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32443  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32444  * @cfg {Number}    width           For East/West panels
32445  * @cfg {Number}    height          For North/South panels
32446  * @cfg {Boolean}   split           To show the splitter
32447  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32448  * 
32449  * @cfg {string}   cls             Extra CSS classes to add to region
32450  * 
32451  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32452  * @cfg {string}   region  the region that it inhabits..
32453  *
32454
32455  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32456  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32457
32458  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32459  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32460  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32461  */
32462 Roo.bootstrap.layout.Region = function(config)
32463 {
32464     this.applyConfig(config);
32465
32466     var mgr = config.mgr;
32467     var pos = config.region;
32468     config.skipConfig = true;
32469     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32470     
32471     if (mgr.el) {
32472         this.onRender(mgr.el);   
32473     }
32474      
32475     this.visible = true;
32476     this.collapsed = false;
32477 };
32478
32479 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32480
32481     position: '', // set by wrapper (eg. north/south etc..)
32482
32483     createBody : function(){
32484         /** This region's body element 
32485         * @type Roo.Element */
32486         this.bodyEl = this.el.createChild({
32487                 tag: "div",
32488                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32489         });
32490     },
32491
32492     onRender: function(ctr, pos)
32493     {
32494         var dh = Roo.DomHelper;
32495         /** This region's container element 
32496         * @type Roo.Element */
32497         this.el = dh.append(ctr.dom, {
32498                 tag: "div",
32499                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32500             }, true);
32501         /** This region's title element 
32502         * @type Roo.Element */
32503     
32504         this.titleEl = dh.append(this.el.dom,
32505             {
32506                     tag: "div",
32507                     unselectable: "on",
32508                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32509                     children:[
32510                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32511                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32512                     ]}, true);
32513         
32514         this.titleEl.enableDisplayMode();
32515         /** This region's title text element 
32516         * @type HTMLElement */
32517         this.titleTextEl = this.titleEl.dom.firstChild;
32518         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32519         /*
32520         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32521         this.closeBtn.enableDisplayMode();
32522         this.closeBtn.on("click", this.closeClicked, this);
32523         this.closeBtn.hide();
32524     */
32525         this.createBody(this.config);
32526         if(this.config.hideWhenEmpty){
32527             this.hide();
32528             this.on("paneladded", this.validateVisibility, this);
32529             this.on("panelremoved", this.validateVisibility, this);
32530         }
32531         if(this.autoScroll){
32532             this.bodyEl.setStyle("overflow", "auto");
32533         }else{
32534             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32535         }
32536         //if(c.titlebar !== false){
32537             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32538                 this.titleEl.hide();
32539             }else{
32540                 this.titleEl.show();
32541                 if(this.config.title){
32542                     this.titleTextEl.innerHTML = this.config.title;
32543                 }
32544             }
32545         //}
32546         if(this.config.collapsed){
32547             this.collapse(true);
32548         }
32549         if(this.config.hidden){
32550             this.hide();
32551         }
32552     },
32553     
32554     applyConfig : function(c)
32555     {
32556         /*
32557          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32558             var dh = Roo.DomHelper;
32559             if(c.titlebar !== false){
32560                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32561                 this.collapseBtn.on("click", this.collapse, this);
32562                 this.collapseBtn.enableDisplayMode();
32563                 /*
32564                 if(c.showPin === true || this.showPin){
32565                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32566                     this.stickBtn.enableDisplayMode();
32567                     this.stickBtn.on("click", this.expand, this);
32568                     this.stickBtn.hide();
32569                 }
32570                 
32571             }
32572             */
32573             /** This region's collapsed element
32574             * @type Roo.Element */
32575             /*
32576              *
32577             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32578                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32579             ]}, true);
32580             
32581             if(c.floatable !== false){
32582                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32583                this.collapsedEl.on("click", this.collapseClick, this);
32584             }
32585
32586             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32587                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32588                    id: "message", unselectable: "on", style:{"float":"left"}});
32589                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32590              }
32591             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32592             this.expandBtn.on("click", this.expand, this);
32593             
32594         }
32595         
32596         if(this.collapseBtn){
32597             this.collapseBtn.setVisible(c.collapsible == true);
32598         }
32599         
32600         this.cmargins = c.cmargins || this.cmargins ||
32601                          (this.position == "west" || this.position == "east" ?
32602                              {top: 0, left: 2, right:2, bottom: 0} :
32603                              {top: 2, left: 0, right:0, bottom: 2});
32604         */
32605         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32606         
32607         
32608         this.bottomTabs = c.tabPosition != "top";
32609         
32610         this.autoScroll = c.autoScroll || false;
32611         
32612         
32613        
32614         
32615         this.duration = c.duration || .30;
32616         this.slideDuration = c.slideDuration || .45;
32617         this.config = c;
32618        
32619     },
32620     /**
32621      * Returns true if this region is currently visible.
32622      * @return {Boolean}
32623      */
32624     isVisible : function(){
32625         return this.visible;
32626     },
32627
32628     /**
32629      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32630      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32631      */
32632     //setCollapsedTitle : function(title){
32633     //    title = title || "&#160;";
32634      //   if(this.collapsedTitleTextEl){
32635       //      this.collapsedTitleTextEl.innerHTML = title;
32636        // }
32637     //},
32638
32639     getBox : function(){
32640         var b;
32641       //  if(!this.collapsed){
32642             b = this.el.getBox(false, true);
32643        // }else{
32644           //  b = this.collapsedEl.getBox(false, true);
32645         //}
32646         return b;
32647     },
32648
32649     getMargins : function(){
32650         return this.margins;
32651         //return this.collapsed ? this.cmargins : this.margins;
32652     },
32653 /*
32654     highlight : function(){
32655         this.el.addClass("x-layout-panel-dragover");
32656     },
32657
32658     unhighlight : function(){
32659         this.el.removeClass("x-layout-panel-dragover");
32660     },
32661 */
32662     updateBox : function(box)
32663     {
32664         this.box = box;
32665         if(!this.collapsed){
32666             this.el.dom.style.left = box.x + "px";
32667             this.el.dom.style.top = box.y + "px";
32668             this.updateBody(box.width, box.height);
32669         }else{
32670             this.collapsedEl.dom.style.left = box.x + "px";
32671             this.collapsedEl.dom.style.top = box.y + "px";
32672             this.collapsedEl.setSize(box.width, box.height);
32673         }
32674         if(this.tabs){
32675             this.tabs.autoSizeTabs();
32676         }
32677     },
32678
32679     updateBody : function(w, h)
32680     {
32681         if(w !== null){
32682             this.el.setWidth(w);
32683             w -= this.el.getBorderWidth("rl");
32684             if(this.config.adjustments){
32685                 w += this.config.adjustments[0];
32686             }
32687         }
32688         if(h !== null){
32689             this.el.setHeight(h);
32690             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32691             h -= this.el.getBorderWidth("tb");
32692             if(this.config.adjustments){
32693                 h += this.config.adjustments[1];
32694             }
32695             this.bodyEl.setHeight(h);
32696             if(this.tabs){
32697                 h = this.tabs.syncHeight(h);
32698             }
32699         }
32700         if(this.panelSize){
32701             w = w !== null ? w : this.panelSize.width;
32702             h = h !== null ? h : this.panelSize.height;
32703         }
32704         if(this.activePanel){
32705             var el = this.activePanel.getEl();
32706             w = w !== null ? w : el.getWidth();
32707             h = h !== null ? h : el.getHeight();
32708             this.panelSize = {width: w, height: h};
32709             this.activePanel.setSize(w, h);
32710         }
32711         if(Roo.isIE && this.tabs){
32712             this.tabs.el.repaint();
32713         }
32714     },
32715
32716     /**
32717      * Returns the container element for this region.
32718      * @return {Roo.Element}
32719      */
32720     getEl : function(){
32721         return this.el;
32722     },
32723
32724     /**
32725      * Hides this region.
32726      */
32727     hide : function(){
32728         //if(!this.collapsed){
32729             this.el.dom.style.left = "-2000px";
32730             this.el.hide();
32731         //}else{
32732          //   this.collapsedEl.dom.style.left = "-2000px";
32733          //   this.collapsedEl.hide();
32734        // }
32735         this.visible = false;
32736         this.fireEvent("visibilitychange", this, false);
32737     },
32738
32739     /**
32740      * Shows this region if it was previously hidden.
32741      */
32742     show : function(){
32743         //if(!this.collapsed){
32744             this.el.show();
32745         //}else{
32746         //    this.collapsedEl.show();
32747        // }
32748         this.visible = true;
32749         this.fireEvent("visibilitychange", this, true);
32750     },
32751 /*
32752     closeClicked : function(){
32753         if(this.activePanel){
32754             this.remove(this.activePanel);
32755         }
32756     },
32757
32758     collapseClick : function(e){
32759         if(this.isSlid){
32760            e.stopPropagation();
32761            this.slideIn();
32762         }else{
32763            e.stopPropagation();
32764            this.slideOut();
32765         }
32766     },
32767 */
32768     /**
32769      * Collapses this region.
32770      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32771      */
32772     /*
32773     collapse : function(skipAnim, skipCheck = false){
32774         if(this.collapsed) {
32775             return;
32776         }
32777         
32778         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32779             
32780             this.collapsed = true;
32781             if(this.split){
32782                 this.split.el.hide();
32783             }
32784             if(this.config.animate && skipAnim !== true){
32785                 this.fireEvent("invalidated", this);
32786                 this.animateCollapse();
32787             }else{
32788                 this.el.setLocation(-20000,-20000);
32789                 this.el.hide();
32790                 this.collapsedEl.show();
32791                 this.fireEvent("collapsed", this);
32792                 this.fireEvent("invalidated", this);
32793             }
32794         }
32795         
32796     },
32797 */
32798     animateCollapse : function(){
32799         // overridden
32800     },
32801
32802     /**
32803      * Expands this region if it was previously collapsed.
32804      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32805      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32806      */
32807     /*
32808     expand : function(e, skipAnim){
32809         if(e) {
32810             e.stopPropagation();
32811         }
32812         if(!this.collapsed || this.el.hasActiveFx()) {
32813             return;
32814         }
32815         if(this.isSlid){
32816             this.afterSlideIn();
32817             skipAnim = true;
32818         }
32819         this.collapsed = false;
32820         if(this.config.animate && skipAnim !== true){
32821             this.animateExpand();
32822         }else{
32823             this.el.show();
32824             if(this.split){
32825                 this.split.el.show();
32826             }
32827             this.collapsedEl.setLocation(-2000,-2000);
32828             this.collapsedEl.hide();
32829             this.fireEvent("invalidated", this);
32830             this.fireEvent("expanded", this);
32831         }
32832     },
32833 */
32834     animateExpand : function(){
32835         // overridden
32836     },
32837
32838     initTabs : function()
32839     {
32840         this.bodyEl.setStyle("overflow", "hidden");
32841         var ts = new Roo.bootstrap.panel.Tabs({
32842                 el: this.bodyEl.dom,
32843                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32844                 disableTooltips: this.config.disableTabTips,
32845                 toolbar : this.config.toolbar
32846             });
32847         
32848         if(this.config.hideTabs){
32849             ts.stripWrap.setDisplayed(false);
32850         }
32851         this.tabs = ts;
32852         ts.resizeTabs = this.config.resizeTabs === true;
32853         ts.minTabWidth = this.config.minTabWidth || 40;
32854         ts.maxTabWidth = this.config.maxTabWidth || 250;
32855         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32856         ts.monitorResize = false;
32857         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32858         ts.bodyEl.addClass('roo-layout-tabs-body');
32859         this.panels.each(this.initPanelAsTab, this);
32860     },
32861
32862     initPanelAsTab : function(panel){
32863         var ti = this.tabs.addTab(
32864                     panel.getEl().id,
32865                     panel.getTitle(), null,
32866                     this.config.closeOnTab && panel.isClosable()
32867             );
32868         if(panel.tabTip !== undefined){
32869             ti.setTooltip(panel.tabTip);
32870         }
32871         ti.on("activate", function(){
32872               this.setActivePanel(panel);
32873         }, this);
32874         
32875         if(this.config.closeOnTab){
32876             ti.on("beforeclose", function(t, e){
32877                 e.cancel = true;
32878                 this.remove(panel);
32879             }, this);
32880         }
32881         return ti;
32882     },
32883
32884     updatePanelTitle : function(panel, title)
32885     {
32886         if(this.activePanel == panel){
32887             this.updateTitle(title);
32888         }
32889         if(this.tabs){
32890             var ti = this.tabs.getTab(panel.getEl().id);
32891             ti.setText(title);
32892             if(panel.tabTip !== undefined){
32893                 ti.setTooltip(panel.tabTip);
32894             }
32895         }
32896     },
32897
32898     updateTitle : function(title){
32899         if(this.titleTextEl && !this.config.title){
32900             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32901         }
32902     },
32903
32904     setActivePanel : function(panel)
32905     {
32906         panel = this.getPanel(panel);
32907         if(this.activePanel && this.activePanel != panel){
32908             this.activePanel.setActiveState(false);
32909         }
32910         this.activePanel = panel;
32911         panel.setActiveState(true);
32912         if(this.panelSize){
32913             panel.setSize(this.panelSize.width, this.panelSize.height);
32914         }
32915         if(this.closeBtn){
32916             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32917         }
32918         this.updateTitle(panel.getTitle());
32919         if(this.tabs){
32920             this.fireEvent("invalidated", this);
32921         }
32922         this.fireEvent("panelactivated", this, panel);
32923     },
32924
32925     /**
32926      * Shows the specified panel.
32927      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32928      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32929      */
32930     showPanel : function(panel)
32931     {
32932         panel = this.getPanel(panel);
32933         if(panel){
32934             if(this.tabs){
32935                 var tab = this.tabs.getTab(panel.getEl().id);
32936                 if(tab.isHidden()){
32937                     this.tabs.unhideTab(tab.id);
32938                 }
32939                 tab.activate();
32940             }else{
32941                 this.setActivePanel(panel);
32942             }
32943         }
32944         return panel;
32945     },
32946
32947     /**
32948      * Get the active panel for this region.
32949      * @return {Roo.ContentPanel} The active panel or null
32950      */
32951     getActivePanel : function(){
32952         return this.activePanel;
32953     },
32954
32955     validateVisibility : function(){
32956         if(this.panels.getCount() < 1){
32957             this.updateTitle("&#160;");
32958             this.closeBtn.hide();
32959             this.hide();
32960         }else{
32961             if(!this.isVisible()){
32962                 this.show();
32963             }
32964         }
32965     },
32966
32967     /**
32968      * Adds the passed ContentPanel(s) to this region.
32969      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32970      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32971      */
32972     add : function(panel){
32973         if(arguments.length > 1){
32974             for(var i = 0, len = arguments.length; i < len; i++) {
32975                 this.add(arguments[i]);
32976             }
32977             return null;
32978         }
32979         if(this.hasPanel(panel)){
32980             this.showPanel(panel);
32981             return panel;
32982         }
32983         panel.setRegion(this);
32984         this.panels.add(panel);
32985         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32986             this.bodyEl.dom.appendChild(panel.getEl().dom);
32987             if(panel.background !== true){
32988                 this.setActivePanel(panel);
32989             }
32990             this.fireEvent("paneladded", this, panel);
32991             return panel;
32992         }
32993         if(!this.tabs){
32994             this.initTabs();
32995         }else{
32996             this.initPanelAsTab(panel);
32997         }
32998         
32999         
33000         if(panel.background !== true){
33001             this.tabs.activate(panel.getEl().id);
33002         }
33003         this.fireEvent("paneladded", this, panel);
33004         return panel;
33005     },
33006
33007     /**
33008      * Hides the tab for the specified panel.
33009      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33010      */
33011     hidePanel : function(panel){
33012         if(this.tabs && (panel = this.getPanel(panel))){
33013             this.tabs.hideTab(panel.getEl().id);
33014         }
33015     },
33016
33017     /**
33018      * Unhides the tab for a previously hidden panel.
33019      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33020      */
33021     unhidePanel : function(panel){
33022         if(this.tabs && (panel = this.getPanel(panel))){
33023             this.tabs.unhideTab(panel.getEl().id);
33024         }
33025     },
33026
33027     clearPanels : function(){
33028         while(this.panels.getCount() > 0){
33029              this.remove(this.panels.first());
33030         }
33031     },
33032
33033     /**
33034      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33035      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33036      * @param {Boolean} preservePanel Overrides the config preservePanel option
33037      * @return {Roo.ContentPanel} The panel that was removed
33038      */
33039     remove : function(panel, preservePanel)
33040     {
33041         panel = this.getPanel(panel);
33042         if(!panel){
33043             return null;
33044         }
33045         var e = {};
33046         this.fireEvent("beforeremove", this, panel, e);
33047         if(e.cancel === true){
33048             return null;
33049         }
33050         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33051         var panelId = panel.getId();
33052         this.panels.removeKey(panelId);
33053         if(preservePanel){
33054             document.body.appendChild(panel.getEl().dom);
33055         }
33056         if(this.tabs){
33057             this.tabs.removeTab(panel.getEl().id);
33058         }else if (!preservePanel){
33059             this.bodyEl.dom.removeChild(panel.getEl().dom);
33060         }
33061         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33062             var p = this.panels.first();
33063             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33064             tempEl.appendChild(p.getEl().dom);
33065             this.bodyEl.update("");
33066             this.bodyEl.dom.appendChild(p.getEl().dom);
33067             tempEl = null;
33068             this.updateTitle(p.getTitle());
33069             this.tabs = null;
33070             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33071             this.setActivePanel(p);
33072         }
33073         panel.setRegion(null);
33074         if(this.activePanel == panel){
33075             this.activePanel = null;
33076         }
33077         if(this.config.autoDestroy !== false && preservePanel !== true){
33078             try{panel.destroy();}catch(e){}
33079         }
33080         this.fireEvent("panelremoved", this, panel);
33081         return panel;
33082     },
33083
33084     /**
33085      * Returns the TabPanel component used by this region
33086      * @return {Roo.TabPanel}
33087      */
33088     getTabs : function(){
33089         return this.tabs;
33090     },
33091
33092     createTool : function(parentEl, className){
33093         var btn = Roo.DomHelper.append(parentEl, {
33094             tag: "div",
33095             cls: "x-layout-tools-button",
33096             children: [ {
33097                 tag: "div",
33098                 cls: "roo-layout-tools-button-inner " + className,
33099                 html: "&#160;"
33100             }]
33101         }, true);
33102         btn.addClassOnOver("roo-layout-tools-button-over");
33103         return btn;
33104     }
33105 });/*
33106  * Based on:
33107  * Ext JS Library 1.1.1
33108  * Copyright(c) 2006-2007, Ext JS, LLC.
33109  *
33110  * Originally Released Under LGPL - original licence link has changed is not relivant.
33111  *
33112  * Fork - LGPL
33113  * <script type="text/javascript">
33114  */
33115  
33116
33117
33118 /**
33119  * @class Roo.SplitLayoutRegion
33120  * @extends Roo.LayoutRegion
33121  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33122  */
33123 Roo.bootstrap.layout.Split = function(config){
33124     this.cursor = config.cursor;
33125     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33126 };
33127
33128 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33129 {
33130     splitTip : "Drag to resize.",
33131     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33132     useSplitTips : false,
33133
33134     applyConfig : function(config){
33135         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33136     },
33137     
33138     onRender : function(ctr,pos) {
33139         
33140         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33141         if(!this.config.split){
33142             return;
33143         }
33144         if(!this.split){
33145             
33146             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33147                             tag: "div",
33148                             id: this.el.id + "-split",
33149                             cls: "roo-layout-split roo-layout-split-"+this.position,
33150                             html: "&#160;"
33151             });
33152             /** The SplitBar for this region 
33153             * @type Roo.SplitBar */
33154             // does not exist yet...
33155             Roo.log([this.position, this.orientation]);
33156             
33157             this.split = new Roo.bootstrap.SplitBar({
33158                 dragElement : splitEl,
33159                 resizingElement: this.el,
33160                 orientation : this.orientation
33161             });
33162             
33163             this.split.on("moved", this.onSplitMove, this);
33164             this.split.useShim = this.config.useShim === true;
33165             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33166             if(this.useSplitTips){
33167                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33168             }
33169             //if(config.collapsible){
33170             //    this.split.el.on("dblclick", this.collapse,  this);
33171             //}
33172         }
33173         if(typeof this.config.minSize != "undefined"){
33174             this.split.minSize = this.config.minSize;
33175         }
33176         if(typeof this.config.maxSize != "undefined"){
33177             this.split.maxSize = this.config.maxSize;
33178         }
33179         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33180             this.hideSplitter();
33181         }
33182         
33183     },
33184
33185     getHMaxSize : function(){
33186          var cmax = this.config.maxSize || 10000;
33187          var center = this.mgr.getRegion("center");
33188          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33189     },
33190
33191     getVMaxSize : function(){
33192          var cmax = this.config.maxSize || 10000;
33193          var center = this.mgr.getRegion("center");
33194          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33195     },
33196
33197     onSplitMove : function(split, newSize){
33198         this.fireEvent("resized", this, newSize);
33199     },
33200     
33201     /** 
33202      * Returns the {@link Roo.SplitBar} for this region.
33203      * @return {Roo.SplitBar}
33204      */
33205     getSplitBar : function(){
33206         return this.split;
33207     },
33208     
33209     hide : function(){
33210         this.hideSplitter();
33211         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33212     },
33213
33214     hideSplitter : function(){
33215         if(this.split){
33216             this.split.el.setLocation(-2000,-2000);
33217             this.split.el.hide();
33218         }
33219     },
33220
33221     show : function(){
33222         if(this.split){
33223             this.split.el.show();
33224         }
33225         Roo.bootstrap.layout.Split.superclass.show.call(this);
33226     },
33227     
33228     beforeSlide: function(){
33229         if(Roo.isGecko){// firefox overflow auto bug workaround
33230             this.bodyEl.clip();
33231             if(this.tabs) {
33232                 this.tabs.bodyEl.clip();
33233             }
33234             if(this.activePanel){
33235                 this.activePanel.getEl().clip();
33236                 
33237                 if(this.activePanel.beforeSlide){
33238                     this.activePanel.beforeSlide();
33239                 }
33240             }
33241         }
33242     },
33243     
33244     afterSlide : function(){
33245         if(Roo.isGecko){// firefox overflow auto bug workaround
33246             this.bodyEl.unclip();
33247             if(this.tabs) {
33248                 this.tabs.bodyEl.unclip();
33249             }
33250             if(this.activePanel){
33251                 this.activePanel.getEl().unclip();
33252                 if(this.activePanel.afterSlide){
33253                     this.activePanel.afterSlide();
33254                 }
33255             }
33256         }
33257     },
33258
33259     initAutoHide : function(){
33260         if(this.autoHide !== false){
33261             if(!this.autoHideHd){
33262                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33263                 this.autoHideHd = {
33264                     "mouseout": function(e){
33265                         if(!e.within(this.el, true)){
33266                             st.delay(500);
33267                         }
33268                     },
33269                     "mouseover" : function(e){
33270                         st.cancel();
33271                     },
33272                     scope : this
33273                 };
33274             }
33275             this.el.on(this.autoHideHd);
33276         }
33277     },
33278
33279     clearAutoHide : function(){
33280         if(this.autoHide !== false){
33281             this.el.un("mouseout", this.autoHideHd.mouseout);
33282             this.el.un("mouseover", this.autoHideHd.mouseover);
33283         }
33284     },
33285
33286     clearMonitor : function(){
33287         Roo.get(document).un("click", this.slideInIf, this);
33288     },
33289
33290     // these names are backwards but not changed for compat
33291     slideOut : function(){
33292         if(this.isSlid || this.el.hasActiveFx()){
33293             return;
33294         }
33295         this.isSlid = true;
33296         if(this.collapseBtn){
33297             this.collapseBtn.hide();
33298         }
33299         this.closeBtnState = this.closeBtn.getStyle('display');
33300         this.closeBtn.hide();
33301         if(this.stickBtn){
33302             this.stickBtn.show();
33303         }
33304         this.el.show();
33305         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33306         this.beforeSlide();
33307         this.el.setStyle("z-index", 10001);
33308         this.el.slideIn(this.getSlideAnchor(), {
33309             callback: function(){
33310                 this.afterSlide();
33311                 this.initAutoHide();
33312                 Roo.get(document).on("click", this.slideInIf, this);
33313                 this.fireEvent("slideshow", this);
33314             },
33315             scope: this,
33316             block: true
33317         });
33318     },
33319
33320     afterSlideIn : function(){
33321         this.clearAutoHide();
33322         this.isSlid = false;
33323         this.clearMonitor();
33324         this.el.setStyle("z-index", "");
33325         if(this.collapseBtn){
33326             this.collapseBtn.show();
33327         }
33328         this.closeBtn.setStyle('display', this.closeBtnState);
33329         if(this.stickBtn){
33330             this.stickBtn.hide();
33331         }
33332         this.fireEvent("slidehide", this);
33333     },
33334
33335     slideIn : function(cb){
33336         if(!this.isSlid || this.el.hasActiveFx()){
33337             Roo.callback(cb);
33338             return;
33339         }
33340         this.isSlid = false;
33341         this.beforeSlide();
33342         this.el.slideOut(this.getSlideAnchor(), {
33343             callback: function(){
33344                 this.el.setLeftTop(-10000, -10000);
33345                 this.afterSlide();
33346                 this.afterSlideIn();
33347                 Roo.callback(cb);
33348             },
33349             scope: this,
33350             block: true
33351         });
33352     },
33353     
33354     slideInIf : function(e){
33355         if(!e.within(this.el)){
33356             this.slideIn();
33357         }
33358     },
33359
33360     animateCollapse : function(){
33361         this.beforeSlide();
33362         this.el.setStyle("z-index", 20000);
33363         var anchor = this.getSlideAnchor();
33364         this.el.slideOut(anchor, {
33365             callback : function(){
33366                 this.el.setStyle("z-index", "");
33367                 this.collapsedEl.slideIn(anchor, {duration:.3});
33368                 this.afterSlide();
33369                 this.el.setLocation(-10000,-10000);
33370                 this.el.hide();
33371                 this.fireEvent("collapsed", this);
33372             },
33373             scope: this,
33374             block: true
33375         });
33376     },
33377
33378     animateExpand : function(){
33379         this.beforeSlide();
33380         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33381         this.el.setStyle("z-index", 20000);
33382         this.collapsedEl.hide({
33383             duration:.1
33384         });
33385         this.el.slideIn(this.getSlideAnchor(), {
33386             callback : function(){
33387                 this.el.setStyle("z-index", "");
33388                 this.afterSlide();
33389                 if(this.split){
33390                     this.split.el.show();
33391                 }
33392                 this.fireEvent("invalidated", this);
33393                 this.fireEvent("expanded", this);
33394             },
33395             scope: this,
33396             block: true
33397         });
33398     },
33399
33400     anchors : {
33401         "west" : "left",
33402         "east" : "right",
33403         "north" : "top",
33404         "south" : "bottom"
33405     },
33406
33407     sanchors : {
33408         "west" : "l",
33409         "east" : "r",
33410         "north" : "t",
33411         "south" : "b"
33412     },
33413
33414     canchors : {
33415         "west" : "tl-tr",
33416         "east" : "tr-tl",
33417         "north" : "tl-bl",
33418         "south" : "bl-tl"
33419     },
33420
33421     getAnchor : function(){
33422         return this.anchors[this.position];
33423     },
33424
33425     getCollapseAnchor : function(){
33426         return this.canchors[this.position];
33427     },
33428
33429     getSlideAnchor : function(){
33430         return this.sanchors[this.position];
33431     },
33432
33433     getAlignAdj : function(){
33434         var cm = this.cmargins;
33435         switch(this.position){
33436             case "west":
33437                 return [0, 0];
33438             break;
33439             case "east":
33440                 return [0, 0];
33441             break;
33442             case "north":
33443                 return [0, 0];
33444             break;
33445             case "south":
33446                 return [0, 0];
33447             break;
33448         }
33449     },
33450
33451     getExpandAdj : function(){
33452         var c = this.collapsedEl, cm = this.cmargins;
33453         switch(this.position){
33454             case "west":
33455                 return [-(cm.right+c.getWidth()+cm.left), 0];
33456             break;
33457             case "east":
33458                 return [cm.right+c.getWidth()+cm.left, 0];
33459             break;
33460             case "north":
33461                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33462             break;
33463             case "south":
33464                 return [0, cm.top+cm.bottom+c.getHeight()];
33465             break;
33466         }
33467     }
33468 });/*
33469  * Based on:
33470  * Ext JS Library 1.1.1
33471  * Copyright(c) 2006-2007, Ext JS, LLC.
33472  *
33473  * Originally Released Under LGPL - original licence link has changed is not relivant.
33474  *
33475  * Fork - LGPL
33476  * <script type="text/javascript">
33477  */
33478 /*
33479  * These classes are private internal classes
33480  */
33481 Roo.bootstrap.layout.Center = function(config){
33482     config.region = "center";
33483     Roo.bootstrap.layout.Region.call(this, config);
33484     this.visible = true;
33485     this.minWidth = config.minWidth || 20;
33486     this.minHeight = config.minHeight || 20;
33487 };
33488
33489 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33490     hide : function(){
33491         // center panel can't be hidden
33492     },
33493     
33494     show : function(){
33495         // center panel can't be hidden
33496     },
33497     
33498     getMinWidth: function(){
33499         return this.minWidth;
33500     },
33501     
33502     getMinHeight: function(){
33503         return this.minHeight;
33504     }
33505 });
33506
33507
33508
33509
33510  
33511
33512
33513
33514
33515
33516 Roo.bootstrap.layout.North = function(config)
33517 {
33518     config.region = 'north';
33519     config.cursor = 'n-resize';
33520     
33521     Roo.bootstrap.layout.Split.call(this, config);
33522     
33523     
33524     if(this.split){
33525         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33526         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33527         this.split.el.addClass("roo-layout-split-v");
33528     }
33529     var size = config.initialSize || config.height;
33530     if(typeof size != "undefined"){
33531         this.el.setHeight(size);
33532     }
33533 };
33534 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33535 {
33536     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33537     
33538     
33539     
33540     getBox : function(){
33541         if(this.collapsed){
33542             return this.collapsedEl.getBox();
33543         }
33544         var box = this.el.getBox();
33545         if(this.split){
33546             box.height += this.split.el.getHeight();
33547         }
33548         return box;
33549     },
33550     
33551     updateBox : function(box){
33552         if(this.split && !this.collapsed){
33553             box.height -= this.split.el.getHeight();
33554             this.split.el.setLeft(box.x);
33555             this.split.el.setTop(box.y+box.height);
33556             this.split.el.setWidth(box.width);
33557         }
33558         if(this.collapsed){
33559             this.updateBody(box.width, null);
33560         }
33561         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33562     }
33563 });
33564
33565
33566
33567
33568
33569 Roo.bootstrap.layout.South = function(config){
33570     config.region = 'south';
33571     config.cursor = 's-resize';
33572     Roo.bootstrap.layout.Split.call(this, config);
33573     if(this.split){
33574         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33575         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33576         this.split.el.addClass("roo-layout-split-v");
33577     }
33578     var size = config.initialSize || config.height;
33579     if(typeof size != "undefined"){
33580         this.el.setHeight(size);
33581     }
33582 };
33583
33584 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33585     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33586     getBox : function(){
33587         if(this.collapsed){
33588             return this.collapsedEl.getBox();
33589         }
33590         var box = this.el.getBox();
33591         if(this.split){
33592             var sh = this.split.el.getHeight();
33593             box.height += sh;
33594             box.y -= sh;
33595         }
33596         return box;
33597     },
33598     
33599     updateBox : function(box){
33600         if(this.split && !this.collapsed){
33601             var sh = this.split.el.getHeight();
33602             box.height -= sh;
33603             box.y += sh;
33604             this.split.el.setLeft(box.x);
33605             this.split.el.setTop(box.y-sh);
33606             this.split.el.setWidth(box.width);
33607         }
33608         if(this.collapsed){
33609             this.updateBody(box.width, null);
33610         }
33611         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33612     }
33613 });
33614
33615 Roo.bootstrap.layout.East = function(config){
33616     config.region = "east";
33617     config.cursor = "e-resize";
33618     Roo.bootstrap.layout.Split.call(this, config);
33619     if(this.split){
33620         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33621         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33622         this.split.el.addClass("roo-layout-split-h");
33623     }
33624     var size = config.initialSize || config.width;
33625     if(typeof size != "undefined"){
33626         this.el.setWidth(size);
33627     }
33628 };
33629 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33630     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33631     getBox : function(){
33632         if(this.collapsed){
33633             return this.collapsedEl.getBox();
33634         }
33635         var box = this.el.getBox();
33636         if(this.split){
33637             var sw = this.split.el.getWidth();
33638             box.width += sw;
33639             box.x -= sw;
33640         }
33641         return box;
33642     },
33643
33644     updateBox : function(box){
33645         if(this.split && !this.collapsed){
33646             var sw = this.split.el.getWidth();
33647             box.width -= sw;
33648             this.split.el.setLeft(box.x);
33649             this.split.el.setTop(box.y);
33650             this.split.el.setHeight(box.height);
33651             box.x += sw;
33652         }
33653         if(this.collapsed){
33654             this.updateBody(null, box.height);
33655         }
33656         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33657     }
33658 });
33659
33660 Roo.bootstrap.layout.West = function(config){
33661     config.region = "west";
33662     config.cursor = "w-resize";
33663     
33664     Roo.bootstrap.layout.Split.call(this, config);
33665     if(this.split){
33666         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33667         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33668         this.split.el.addClass("roo-layout-split-h");
33669     }
33670     
33671 };
33672 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33673     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33674     
33675     onRender: function(ctr, pos)
33676     {
33677         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33678         var size = this.config.initialSize || this.config.width;
33679         if(typeof size != "undefined"){
33680             this.el.setWidth(size);
33681         }
33682     },
33683     
33684     getBox : function(){
33685         if(this.collapsed){
33686             return this.collapsedEl.getBox();
33687         }
33688         var box = this.el.getBox();
33689         if(this.split){
33690             box.width += this.split.el.getWidth();
33691         }
33692         return box;
33693     },
33694     
33695     updateBox : function(box){
33696         if(this.split && !this.collapsed){
33697             var sw = this.split.el.getWidth();
33698             box.width -= sw;
33699             this.split.el.setLeft(box.x+box.width);
33700             this.split.el.setTop(box.y);
33701             this.split.el.setHeight(box.height);
33702         }
33703         if(this.collapsed){
33704             this.updateBody(null, box.height);
33705         }
33706         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33707     }
33708 });
33709 Roo.namespace("Roo.bootstrap.panel");/*
33710  * Based on:
33711  * Ext JS Library 1.1.1
33712  * Copyright(c) 2006-2007, Ext JS, LLC.
33713  *
33714  * Originally Released Under LGPL - original licence link has changed is not relivant.
33715  *
33716  * Fork - LGPL
33717  * <script type="text/javascript">
33718  */
33719 /**
33720  * @class Roo.ContentPanel
33721  * @extends Roo.util.Observable
33722  * A basic ContentPanel element.
33723  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33724  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33725  * @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
33726  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33727  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33728  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33729  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33730  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33731  * @cfg {String} title          The title for this panel
33732  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33733  * @cfg {String} url            Calls {@link #setUrl} with this value
33734  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33735  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33736  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33737  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33738
33739  * @constructor
33740  * Create a new ContentPanel.
33741  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33742  * @param {String/Object} config A string to set only the title or a config object
33743  * @param {String} content (optional) Set the HTML content for this panel
33744  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33745  */
33746 Roo.bootstrap.panel.Content = function( config){
33747     
33748     var el = config.el;
33749     var content = config.content;
33750
33751     if(config.autoCreate){ // xtype is available if this is called from factory
33752         el = Roo.id();
33753     }
33754     this.el = Roo.get(el);
33755     if(!this.el && config && config.autoCreate){
33756         if(typeof config.autoCreate == "object"){
33757             if(!config.autoCreate.id){
33758                 config.autoCreate.id = config.id||el;
33759             }
33760             this.el = Roo.DomHelper.append(document.body,
33761                         config.autoCreate, true);
33762         }else{
33763             var elcfg =  {   tag: "div",
33764                             cls: "roo-layout-inactive-content",
33765                             id: config.id||el
33766                             };
33767             if (config.html) {
33768                 elcfg.html = config.html;
33769                 
33770             }
33771                         
33772             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33773         }
33774     } 
33775     this.closable = false;
33776     this.loaded = false;
33777     this.active = false;
33778    
33779       
33780     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33781         
33782         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33783         
33784         this.wrapEl = this.el.wrap();
33785         var ti = [];
33786         if (config.toolbar.items) {
33787             ti = config.toolbar.items ;
33788             delete config.toolbar.items ;
33789         }
33790         
33791         var nitems = [];
33792         this.toolbar.render(this.wrapEl, 'before');
33793         for(var i =0;i < ti.length;i++) {
33794           //  Roo.log(['add child', items[i]]);
33795             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33796         }
33797         this.toolbar.items = nitems;
33798         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33799         delete config.toolbar;
33800         
33801     }
33802     /*
33803     // xtype created footer. - not sure if will work as we normally have to render first..
33804     if (this.footer && !this.footer.el && this.footer.xtype) {
33805         if (!this.wrapEl) {
33806             this.wrapEl = this.el.wrap();
33807         }
33808     
33809         this.footer.container = this.wrapEl.createChild();
33810          
33811         this.footer = Roo.factory(this.footer, Roo);
33812         
33813     }
33814     */
33815     
33816      if(typeof config == "string"){
33817         this.title = config;
33818     }else{
33819         Roo.apply(this, config);
33820     }
33821     
33822     if(this.resizeEl){
33823         this.resizeEl = Roo.get(this.resizeEl, true);
33824     }else{
33825         this.resizeEl = this.el;
33826     }
33827     // handle view.xtype
33828     
33829  
33830     
33831     
33832     this.addEvents({
33833         /**
33834          * @event activate
33835          * Fires when this panel is activated. 
33836          * @param {Roo.ContentPanel} this
33837          */
33838         "activate" : true,
33839         /**
33840          * @event deactivate
33841          * Fires when this panel is activated. 
33842          * @param {Roo.ContentPanel} this
33843          */
33844         "deactivate" : true,
33845
33846         /**
33847          * @event resize
33848          * Fires when this panel is resized if fitToFrame is true.
33849          * @param {Roo.ContentPanel} this
33850          * @param {Number} width The width after any component adjustments
33851          * @param {Number} height The height after any component adjustments
33852          */
33853         "resize" : true,
33854         
33855          /**
33856          * @event render
33857          * Fires when this tab is created
33858          * @param {Roo.ContentPanel} this
33859          */
33860         "render" : true
33861         
33862         
33863         
33864     });
33865     
33866
33867     
33868     
33869     if(this.autoScroll){
33870         this.resizeEl.setStyle("overflow", "auto");
33871     } else {
33872         // fix randome scrolling
33873         //this.el.on('scroll', function() {
33874         //    Roo.log('fix random scolling');
33875         //    this.scrollTo('top',0); 
33876         //});
33877     }
33878     content = content || this.content;
33879     if(content){
33880         this.setContent(content);
33881     }
33882     if(config && config.url){
33883         this.setUrl(this.url, this.params, this.loadOnce);
33884     }
33885     
33886     
33887     
33888     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33889     
33890     if (this.view && typeof(this.view.xtype) != 'undefined') {
33891         this.view.el = this.el.appendChild(document.createElement("div"));
33892         this.view = Roo.factory(this.view); 
33893         this.view.render  &&  this.view.render(false, '');  
33894     }
33895     
33896     
33897     this.fireEvent('render', this);
33898 };
33899
33900 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33901     tabTip:'',
33902     setRegion : function(region){
33903         this.region = region;
33904         if(region){
33905            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33906         }else{
33907            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33908         } 
33909     },
33910     
33911     /**
33912      * Returns the toolbar for this Panel if one was configured. 
33913      * @return {Roo.Toolbar} 
33914      */
33915     getToolbar : function(){
33916         return this.toolbar;
33917     },
33918     
33919     setActiveState : function(active){
33920         this.active = active;
33921         if(!active){
33922             this.fireEvent("deactivate", this);
33923         }else{
33924             this.fireEvent("activate", this);
33925         }
33926     },
33927     /**
33928      * Updates this panel's element
33929      * @param {String} content The new content
33930      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33931     */
33932     setContent : function(content, loadScripts){
33933         this.el.update(content, loadScripts);
33934     },
33935
33936     ignoreResize : function(w, h){
33937         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33938             return true;
33939         }else{
33940             this.lastSize = {width: w, height: h};
33941             return false;
33942         }
33943     },
33944     /**
33945      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33946      * @return {Roo.UpdateManager} The UpdateManager
33947      */
33948     getUpdateManager : function(){
33949         return this.el.getUpdateManager();
33950     },
33951      /**
33952      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33953      * @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:
33954 <pre><code>
33955 panel.load({
33956     url: "your-url.php",
33957     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33958     callback: yourFunction,
33959     scope: yourObject, //(optional scope)
33960     discardUrl: false,
33961     nocache: false,
33962     text: "Loading...",
33963     timeout: 30,
33964     scripts: false
33965 });
33966 </code></pre>
33967      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33968      * 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.
33969      * @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}
33970      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33971      * @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.
33972      * @return {Roo.ContentPanel} this
33973      */
33974     load : function(){
33975         var um = this.el.getUpdateManager();
33976         um.update.apply(um, arguments);
33977         return this;
33978     },
33979
33980
33981     /**
33982      * 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.
33983      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33984      * @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)
33985      * @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)
33986      * @return {Roo.UpdateManager} The UpdateManager
33987      */
33988     setUrl : function(url, params, loadOnce){
33989         if(this.refreshDelegate){
33990             this.removeListener("activate", this.refreshDelegate);
33991         }
33992         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33993         this.on("activate", this.refreshDelegate);
33994         return this.el.getUpdateManager();
33995     },
33996     
33997     _handleRefresh : function(url, params, loadOnce){
33998         if(!loadOnce || !this.loaded){
33999             var updater = this.el.getUpdateManager();
34000             updater.update(url, params, this._setLoaded.createDelegate(this));
34001         }
34002     },
34003     
34004     _setLoaded : function(){
34005         this.loaded = true;
34006     }, 
34007     
34008     /**
34009      * Returns this panel's id
34010      * @return {String} 
34011      */
34012     getId : function(){
34013         return this.el.id;
34014     },
34015     
34016     /** 
34017      * Returns this panel's element - used by regiosn to add.
34018      * @return {Roo.Element} 
34019      */
34020     getEl : function(){
34021         return this.wrapEl || this.el;
34022     },
34023     
34024    
34025     
34026     adjustForComponents : function(width, height)
34027     {
34028         //Roo.log('adjustForComponents ');
34029         if(this.resizeEl != this.el){
34030             width -= this.el.getFrameWidth('lr');
34031             height -= this.el.getFrameWidth('tb');
34032         }
34033         if(this.toolbar){
34034             var te = this.toolbar.getEl();
34035             height -= te.getHeight();
34036             te.setWidth(width);
34037         }
34038         if(this.footer){
34039             var te = this.footer.getEl();
34040             Roo.log("footer:" + te.getHeight());
34041             
34042             height -= te.getHeight();
34043             te.setWidth(width);
34044         }
34045         
34046         
34047         if(this.adjustments){
34048             width += this.adjustments[0];
34049             height += this.adjustments[1];
34050         }
34051         return {"width": width, "height": height};
34052     },
34053     
34054     setSize : function(width, height){
34055         if(this.fitToFrame && !this.ignoreResize(width, height)){
34056             if(this.fitContainer && this.resizeEl != this.el){
34057                 this.el.setSize(width, height);
34058             }
34059             var size = this.adjustForComponents(width, height);
34060             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34061             this.fireEvent('resize', this, size.width, size.height);
34062         }
34063     },
34064     
34065     /**
34066      * Returns this panel's title
34067      * @return {String} 
34068      */
34069     getTitle : function(){
34070         return this.title;
34071     },
34072     
34073     /**
34074      * Set this panel's title
34075      * @param {String} title
34076      */
34077     setTitle : function(title){
34078         this.title = title;
34079         if(this.region){
34080             this.region.updatePanelTitle(this, title);
34081         }
34082     },
34083     
34084     /**
34085      * Returns true is this panel was configured to be closable
34086      * @return {Boolean} 
34087      */
34088     isClosable : function(){
34089         return this.closable;
34090     },
34091     
34092     beforeSlide : function(){
34093         this.el.clip();
34094         this.resizeEl.clip();
34095     },
34096     
34097     afterSlide : function(){
34098         this.el.unclip();
34099         this.resizeEl.unclip();
34100     },
34101     
34102     /**
34103      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34104      *   Will fail silently if the {@link #setUrl} method has not been called.
34105      *   This does not activate the panel, just updates its content.
34106      */
34107     refresh : function(){
34108         if(this.refreshDelegate){
34109            this.loaded = false;
34110            this.refreshDelegate();
34111         }
34112     },
34113     
34114     /**
34115      * Destroys this panel
34116      */
34117     destroy : function(){
34118         this.el.removeAllListeners();
34119         var tempEl = document.createElement("span");
34120         tempEl.appendChild(this.el.dom);
34121         tempEl.innerHTML = "";
34122         this.el.remove();
34123         this.el = null;
34124     },
34125     
34126     /**
34127      * form - if the content panel contains a form - this is a reference to it.
34128      * @type {Roo.form.Form}
34129      */
34130     form : false,
34131     /**
34132      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34133      *    This contains a reference to it.
34134      * @type {Roo.View}
34135      */
34136     view : false,
34137     
34138       /**
34139      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34140      * <pre><code>
34141
34142 layout.addxtype({
34143        xtype : 'Form',
34144        items: [ .... ]
34145    }
34146 );
34147
34148 </code></pre>
34149      * @param {Object} cfg Xtype definition of item to add.
34150      */
34151     
34152     
34153     getChildContainer: function () {
34154         return this.getEl();
34155     }
34156     
34157     
34158     /*
34159         var  ret = new Roo.factory(cfg);
34160         return ret;
34161         
34162         
34163         // add form..
34164         if (cfg.xtype.match(/^Form$/)) {
34165             
34166             var el;
34167             //if (this.footer) {
34168             //    el = this.footer.container.insertSibling(false, 'before');
34169             //} else {
34170                 el = this.el.createChild();
34171             //}
34172
34173             this.form = new  Roo.form.Form(cfg);
34174             
34175             
34176             if ( this.form.allItems.length) {
34177                 this.form.render(el.dom);
34178             }
34179             return this.form;
34180         }
34181         // should only have one of theses..
34182         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34183             // views.. should not be just added - used named prop 'view''
34184             
34185             cfg.el = this.el.appendChild(document.createElement("div"));
34186             // factory?
34187             
34188             var ret = new Roo.factory(cfg);
34189              
34190              ret.render && ret.render(false, ''); // render blank..
34191             this.view = ret;
34192             return ret;
34193         }
34194         return false;
34195     }
34196     \*/
34197 });
34198  
34199 /**
34200  * @class Roo.bootstrap.panel.Grid
34201  * @extends Roo.bootstrap.panel.Content
34202  * @constructor
34203  * Create a new GridPanel.
34204  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34205  * @param {Object} config A the config object
34206   
34207  */
34208
34209
34210
34211 Roo.bootstrap.panel.Grid = function(config)
34212 {
34213     
34214       
34215     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34216         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34217
34218     config.el = this.wrapper;
34219     //this.el = this.wrapper;
34220     
34221       if (config.container) {
34222         // ctor'ed from a Border/panel.grid
34223         
34224         
34225         this.wrapper.setStyle("overflow", "hidden");
34226         this.wrapper.addClass('roo-grid-container');
34227
34228     }
34229     
34230     
34231     if(config.toolbar){
34232         var tool_el = this.wrapper.createChild();    
34233         this.toolbar = Roo.factory(config.toolbar);
34234         var ti = [];
34235         if (config.toolbar.items) {
34236             ti = config.toolbar.items ;
34237             delete config.toolbar.items ;
34238         }
34239         
34240         var nitems = [];
34241         this.toolbar.render(tool_el);
34242         for(var i =0;i < ti.length;i++) {
34243           //  Roo.log(['add child', items[i]]);
34244             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34245         }
34246         this.toolbar.items = nitems;
34247         
34248         delete config.toolbar;
34249     }
34250     
34251     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34252     config.grid.scrollBody = true;;
34253     config.grid.monitorWindowResize = false; // turn off autosizing
34254     config.grid.autoHeight = false;
34255     config.grid.autoWidth = false;
34256     
34257     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34258     
34259     if (config.background) {
34260         // render grid on panel activation (if panel background)
34261         this.on('activate', function(gp) {
34262             if (!gp.grid.rendered) {
34263                 gp.grid.render(el);
34264                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34265
34266             }
34267         });
34268             
34269     } else {
34270         this.grid.render(this.wrapper);
34271         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34272
34273     }
34274     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34275     // ??? needed ??? config.el = this.wrapper;
34276     
34277     
34278     
34279   
34280     // xtype created footer. - not sure if will work as we normally have to render first..
34281     if (this.footer && !this.footer.el && this.footer.xtype) {
34282         
34283         var ctr = this.grid.getView().getFooterPanel(true);
34284         this.footer.dataSource = this.grid.dataSource;
34285         this.footer = Roo.factory(this.footer, Roo);
34286         this.footer.render(ctr);
34287         
34288     }
34289     
34290     
34291     
34292     
34293      
34294 };
34295
34296 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34297     getId : function(){
34298         return this.grid.id;
34299     },
34300     
34301     /**
34302      * Returns the grid for this panel
34303      * @return {Roo.bootstrap.Table} 
34304      */
34305     getGrid : function(){
34306         return this.grid;    
34307     },
34308     
34309     setSize : function(width, height){
34310         if(!this.ignoreResize(width, height)){
34311             var grid = this.grid;
34312             var size = this.adjustForComponents(width, height);
34313             var gridel = grid.getGridEl();
34314             gridel.setSize(size.width, size.height);
34315             /*
34316             var thd = grid.getGridEl().select('thead',true).first();
34317             var tbd = grid.getGridEl().select('tbody', true).first();
34318             if (tbd) {
34319                 tbd.setSize(width, height - thd.getHeight());
34320             }
34321             */
34322             grid.autoSize();
34323         }
34324     },
34325      
34326     
34327     
34328     beforeSlide : function(){
34329         this.grid.getView().scroller.clip();
34330     },
34331     
34332     afterSlide : function(){
34333         this.grid.getView().scroller.unclip();
34334     },
34335     
34336     destroy : function(){
34337         this.grid.destroy();
34338         delete this.grid;
34339         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34340     }
34341 });
34342
34343 /**
34344  * @class Roo.bootstrap.panel.Nest
34345  * @extends Roo.bootstrap.panel.Content
34346  * @constructor
34347  * Create a new Panel, that can contain a layout.Border.
34348  * 
34349  * 
34350  * @param {Roo.BorderLayout} layout The layout for this panel
34351  * @param {String/Object} config A string to set only the title or a config object
34352  */
34353 Roo.bootstrap.panel.Nest = function(config)
34354 {
34355     // construct with only one argument..
34356     /* FIXME - implement nicer consturctors
34357     if (layout.layout) {
34358         config = layout;
34359         layout = config.layout;
34360         delete config.layout;
34361     }
34362     if (layout.xtype && !layout.getEl) {
34363         // then layout needs constructing..
34364         layout = Roo.factory(layout, Roo);
34365     }
34366     */
34367     
34368     config.el =  config.layout.getEl();
34369     
34370     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34371     
34372     config.layout.monitorWindowResize = false; // turn off autosizing
34373     this.layout = config.layout;
34374     this.layout.getEl().addClass("roo-layout-nested-layout");
34375     
34376     
34377     
34378     
34379 };
34380
34381 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34382
34383     setSize : function(width, height){
34384         if(!this.ignoreResize(width, height)){
34385             var size = this.adjustForComponents(width, height);
34386             var el = this.layout.getEl();
34387             el.setSize(size.width, size.height);
34388             var touch = el.dom.offsetWidth;
34389             this.layout.layout();
34390             // ie requires a double layout on the first pass
34391             if(Roo.isIE && !this.initialized){
34392                 this.initialized = true;
34393                 this.layout.layout();
34394             }
34395         }
34396     },
34397     
34398     // activate all subpanels if not currently active..
34399     
34400     setActiveState : function(active){
34401         this.active = active;
34402         if(!active){
34403             this.fireEvent("deactivate", this);
34404             return;
34405         }
34406         
34407         this.fireEvent("activate", this);
34408         // not sure if this should happen before or after..
34409         if (!this.layout) {
34410             return; // should not happen..
34411         }
34412         var reg = false;
34413         for (var r in this.layout.regions) {
34414             reg = this.layout.getRegion(r);
34415             if (reg.getActivePanel()) {
34416                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34417                 reg.setActivePanel(reg.getActivePanel());
34418                 continue;
34419             }
34420             if (!reg.panels.length) {
34421                 continue;
34422             }
34423             reg.showPanel(reg.getPanel(0));
34424         }
34425         
34426         
34427         
34428         
34429     },
34430     
34431     /**
34432      * Returns the nested BorderLayout for this panel
34433      * @return {Roo.BorderLayout} 
34434      */
34435     getLayout : function(){
34436         return this.layout;
34437     },
34438     
34439      /**
34440      * Adds a xtype elements to the layout of the nested panel
34441      * <pre><code>
34442
34443 panel.addxtype({
34444        xtype : 'ContentPanel',
34445        region: 'west',
34446        items: [ .... ]
34447    }
34448 );
34449
34450 panel.addxtype({
34451         xtype : 'NestedLayoutPanel',
34452         region: 'west',
34453         layout: {
34454            center: { },
34455            west: { }   
34456         },
34457         items : [ ... list of content panels or nested layout panels.. ]
34458    }
34459 );
34460 </code></pre>
34461      * @param {Object} cfg Xtype definition of item to add.
34462      */
34463     addxtype : function(cfg) {
34464         return this.layout.addxtype(cfg);
34465     
34466     }
34467 });        /*
34468  * Based on:
34469  * Ext JS Library 1.1.1
34470  * Copyright(c) 2006-2007, Ext JS, LLC.
34471  *
34472  * Originally Released Under LGPL - original licence link has changed is not relivant.
34473  *
34474  * Fork - LGPL
34475  * <script type="text/javascript">
34476  */
34477 /**
34478  * @class Roo.TabPanel
34479  * @extends Roo.util.Observable
34480  * A lightweight tab container.
34481  * <br><br>
34482  * Usage:
34483  * <pre><code>
34484 // basic tabs 1, built from existing content
34485 var tabs = new Roo.TabPanel("tabs1");
34486 tabs.addTab("script", "View Script");
34487 tabs.addTab("markup", "View Markup");
34488 tabs.activate("script");
34489
34490 // more advanced tabs, built from javascript
34491 var jtabs = new Roo.TabPanel("jtabs");
34492 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34493
34494 // set up the UpdateManager
34495 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34496 var updater = tab2.getUpdateManager();
34497 updater.setDefaultUrl("ajax1.htm");
34498 tab2.on('activate', updater.refresh, updater, true);
34499
34500 // Use setUrl for Ajax loading
34501 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34502 tab3.setUrl("ajax2.htm", null, true);
34503
34504 // Disabled tab
34505 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34506 tab4.disable();
34507
34508 jtabs.activate("jtabs-1");
34509  * </code></pre>
34510  * @constructor
34511  * Create a new TabPanel.
34512  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34513  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34514  */
34515 Roo.bootstrap.panel.Tabs = function(config){
34516     /**
34517     * The container element for this TabPanel.
34518     * @type Roo.Element
34519     */
34520     this.el = Roo.get(config.el);
34521     delete config.el;
34522     if(config){
34523         if(typeof config == "boolean"){
34524             this.tabPosition = config ? "bottom" : "top";
34525         }else{
34526             Roo.apply(this, config);
34527         }
34528     }
34529     
34530     if(this.tabPosition == "bottom"){
34531         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34532         this.el.addClass("roo-tabs-bottom");
34533     }
34534     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34535     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34536     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34537     if(Roo.isIE){
34538         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34539     }
34540     if(this.tabPosition != "bottom"){
34541         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34542          * @type Roo.Element
34543          */
34544         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34545         this.el.addClass("roo-tabs-top");
34546     }
34547     this.items = [];
34548
34549     this.bodyEl.setStyle("position", "relative");
34550
34551     this.active = null;
34552     this.activateDelegate = this.activate.createDelegate(this);
34553
34554     this.addEvents({
34555         /**
34556          * @event tabchange
34557          * Fires when the active tab changes
34558          * @param {Roo.TabPanel} this
34559          * @param {Roo.TabPanelItem} activePanel The new active tab
34560          */
34561         "tabchange": true,
34562         /**
34563          * @event beforetabchange
34564          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34565          * @param {Roo.TabPanel} this
34566          * @param {Object} e Set cancel to true on this object to cancel the tab change
34567          * @param {Roo.TabPanelItem} tab The tab being changed to
34568          */
34569         "beforetabchange" : true
34570     });
34571
34572     Roo.EventManager.onWindowResize(this.onResize, this);
34573     this.cpad = this.el.getPadding("lr");
34574     this.hiddenCount = 0;
34575
34576
34577     // toolbar on the tabbar support...
34578     if (this.toolbar) {
34579         alert("no toolbar support yet");
34580         this.toolbar  = false;
34581         /*
34582         var tcfg = this.toolbar;
34583         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34584         this.toolbar = new Roo.Toolbar(tcfg);
34585         if (Roo.isSafari) {
34586             var tbl = tcfg.container.child('table', true);
34587             tbl.setAttribute('width', '100%');
34588         }
34589         */
34590         
34591     }
34592    
34593
34594
34595     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34596 };
34597
34598 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34599     /*
34600      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34601      */
34602     tabPosition : "top",
34603     /*
34604      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34605      */
34606     currentTabWidth : 0,
34607     /*
34608      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34609      */
34610     minTabWidth : 40,
34611     /*
34612      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34613      */
34614     maxTabWidth : 250,
34615     /*
34616      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34617      */
34618     preferredTabWidth : 175,
34619     /*
34620      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34621      */
34622     resizeTabs : false,
34623     /*
34624      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34625      */
34626     monitorResize : true,
34627     /*
34628      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34629      */
34630     toolbar : false,
34631
34632     /**
34633      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34634      * @param {String} id The id of the div to use <b>or create</b>
34635      * @param {String} text The text for the tab
34636      * @param {String} content (optional) Content to put in the TabPanelItem body
34637      * @param {Boolean} closable (optional) True to create a close icon on the tab
34638      * @return {Roo.TabPanelItem} The created TabPanelItem
34639      */
34640     addTab : function(id, text, content, closable)
34641     {
34642         var item = new Roo.bootstrap.panel.TabItem({
34643             panel: this,
34644             id : id,
34645             text : text,
34646             closable : closable
34647         });
34648         this.addTabItem(item);
34649         if(content){
34650             item.setContent(content);
34651         }
34652         return item;
34653     },
34654
34655     /**
34656      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34657      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34658      * @return {Roo.TabPanelItem}
34659      */
34660     getTab : function(id){
34661         return this.items[id];
34662     },
34663
34664     /**
34665      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34666      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34667      */
34668     hideTab : function(id){
34669         var t = this.items[id];
34670         if(!t.isHidden()){
34671            t.setHidden(true);
34672            this.hiddenCount++;
34673            this.autoSizeTabs();
34674         }
34675     },
34676
34677     /**
34678      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34679      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34680      */
34681     unhideTab : function(id){
34682         var t = this.items[id];
34683         if(t.isHidden()){
34684            t.setHidden(false);
34685            this.hiddenCount--;
34686            this.autoSizeTabs();
34687         }
34688     },
34689
34690     /**
34691      * Adds an existing {@link Roo.TabPanelItem}.
34692      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34693      */
34694     addTabItem : function(item){
34695         this.items[item.id] = item;
34696         this.items.push(item);
34697       //  if(this.resizeTabs){
34698     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34699   //         this.autoSizeTabs();
34700 //        }else{
34701 //            item.autoSize();
34702        // }
34703     },
34704
34705     /**
34706      * Removes a {@link Roo.TabPanelItem}.
34707      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34708      */
34709     removeTab : function(id){
34710         var items = this.items;
34711         var tab = items[id];
34712         if(!tab) { return; }
34713         var index = items.indexOf(tab);
34714         if(this.active == tab && items.length > 1){
34715             var newTab = this.getNextAvailable(index);
34716             if(newTab) {
34717                 newTab.activate();
34718             }
34719         }
34720         this.stripEl.dom.removeChild(tab.pnode.dom);
34721         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34722             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34723         }
34724         items.splice(index, 1);
34725         delete this.items[tab.id];
34726         tab.fireEvent("close", tab);
34727         tab.purgeListeners();
34728         this.autoSizeTabs();
34729     },
34730
34731     getNextAvailable : function(start){
34732         var items = this.items;
34733         var index = start;
34734         // look for a next tab that will slide over to
34735         // replace the one being removed
34736         while(index < items.length){
34737             var item = items[++index];
34738             if(item && !item.isHidden()){
34739                 return item;
34740             }
34741         }
34742         // if one isn't found select the previous tab (on the left)
34743         index = start;
34744         while(index >= 0){
34745             var item = items[--index];
34746             if(item && !item.isHidden()){
34747                 return item;
34748             }
34749         }
34750         return null;
34751     },
34752
34753     /**
34754      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34755      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34756      */
34757     disableTab : function(id){
34758         var tab = this.items[id];
34759         if(tab && this.active != tab){
34760             tab.disable();
34761         }
34762     },
34763
34764     /**
34765      * Enables a {@link Roo.TabPanelItem} that is disabled.
34766      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34767      */
34768     enableTab : function(id){
34769         var tab = this.items[id];
34770         tab.enable();
34771     },
34772
34773     /**
34774      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34775      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34776      * @return {Roo.TabPanelItem} The TabPanelItem.
34777      */
34778     activate : function(id){
34779         var tab = this.items[id];
34780         if(!tab){
34781             return null;
34782         }
34783         if(tab == this.active || tab.disabled){
34784             return tab;
34785         }
34786         var e = {};
34787         this.fireEvent("beforetabchange", this, e, tab);
34788         if(e.cancel !== true && !tab.disabled){
34789             if(this.active){
34790                 this.active.hide();
34791             }
34792             this.active = this.items[id];
34793             this.active.show();
34794             this.fireEvent("tabchange", this, this.active);
34795         }
34796         return tab;
34797     },
34798
34799     /**
34800      * Gets the active {@link Roo.TabPanelItem}.
34801      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34802      */
34803     getActiveTab : function(){
34804         return this.active;
34805     },
34806
34807     /**
34808      * Updates the tab body element to fit the height of the container element
34809      * for overflow scrolling
34810      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34811      */
34812     syncHeight : function(targetHeight){
34813         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34814         var bm = this.bodyEl.getMargins();
34815         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34816         this.bodyEl.setHeight(newHeight);
34817         return newHeight;
34818     },
34819
34820     onResize : function(){
34821         if(this.monitorResize){
34822             this.autoSizeTabs();
34823         }
34824     },
34825
34826     /**
34827      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34828      */
34829     beginUpdate : function(){
34830         this.updating = true;
34831     },
34832
34833     /**
34834      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34835      */
34836     endUpdate : function(){
34837         this.updating = false;
34838         this.autoSizeTabs();
34839     },
34840
34841     /**
34842      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34843      */
34844     autoSizeTabs : function(){
34845         var count = this.items.length;
34846         var vcount = count - this.hiddenCount;
34847         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34848             return;
34849         }
34850         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34851         var availWidth = Math.floor(w / vcount);
34852         var b = this.stripBody;
34853         if(b.getWidth() > w){
34854             var tabs = this.items;
34855             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34856             if(availWidth < this.minTabWidth){
34857                 /*if(!this.sleft){    // incomplete scrolling code
34858                     this.createScrollButtons();
34859                 }
34860                 this.showScroll();
34861                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34862             }
34863         }else{
34864             if(this.currentTabWidth < this.preferredTabWidth){
34865                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34866             }
34867         }
34868     },
34869
34870     /**
34871      * Returns the number of tabs in this TabPanel.
34872      * @return {Number}
34873      */
34874      getCount : function(){
34875          return this.items.length;
34876      },
34877
34878     /**
34879      * Resizes all the tabs to the passed width
34880      * @param {Number} The new width
34881      */
34882     setTabWidth : function(width){
34883         this.currentTabWidth = width;
34884         for(var i = 0, len = this.items.length; i < len; i++) {
34885                 if(!this.items[i].isHidden()) {
34886                 this.items[i].setWidth(width);
34887             }
34888         }
34889     },
34890
34891     /**
34892      * Destroys this TabPanel
34893      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34894      */
34895     destroy : function(removeEl){
34896         Roo.EventManager.removeResizeListener(this.onResize, this);
34897         for(var i = 0, len = this.items.length; i < len; i++){
34898             this.items[i].purgeListeners();
34899         }
34900         if(removeEl === true){
34901             this.el.update("");
34902             this.el.remove();
34903         }
34904     },
34905     
34906     createStrip : function(container)
34907     {
34908         var strip = document.createElement("nav");
34909         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34910         container.appendChild(strip);
34911         return strip;
34912     },
34913     
34914     createStripList : function(strip)
34915     {
34916         // div wrapper for retard IE
34917         // returns the "tr" element.
34918         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34919         //'<div class="x-tabs-strip-wrap">'+
34920           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34921           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34922         return strip.firstChild; //.firstChild.firstChild.firstChild;
34923     },
34924     createBody : function(container)
34925     {
34926         var body = document.createElement("div");
34927         Roo.id(body, "tab-body");
34928         //Roo.fly(body).addClass("x-tabs-body");
34929         Roo.fly(body).addClass("tab-content");
34930         container.appendChild(body);
34931         return body;
34932     },
34933     createItemBody :function(bodyEl, id){
34934         var body = Roo.getDom(id);
34935         if(!body){
34936             body = document.createElement("div");
34937             body.id = id;
34938         }
34939         //Roo.fly(body).addClass("x-tabs-item-body");
34940         Roo.fly(body).addClass("tab-pane");
34941          bodyEl.insertBefore(body, bodyEl.firstChild);
34942         return body;
34943     },
34944     /** @private */
34945     createStripElements :  function(stripEl, text, closable)
34946     {
34947         var td = document.createElement("li"); // was td..
34948         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34949         //stripEl.appendChild(td);
34950         /*if(closable){
34951             td.className = "x-tabs-closable";
34952             if(!this.closeTpl){
34953                 this.closeTpl = new Roo.Template(
34954                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34955                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34956                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34957                 );
34958             }
34959             var el = this.closeTpl.overwrite(td, {"text": text});
34960             var close = el.getElementsByTagName("div")[0];
34961             var inner = el.getElementsByTagName("em")[0];
34962             return {"el": el, "close": close, "inner": inner};
34963         } else {
34964         */
34965         // not sure what this is..
34966             if(!this.tabTpl){
34967                 //this.tabTpl = new Roo.Template(
34968                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34969                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34970                 //);
34971                 this.tabTpl = new Roo.Template(
34972                    '<a href="#">' +
34973                    '<span unselectable="on"' +
34974                             (this.disableTooltips ? '' : ' title="{text}"') +
34975                             ' >{text}</span></span></a>'
34976                 );
34977                 
34978             }
34979             var el = this.tabTpl.overwrite(td, {"text": text});
34980             var inner = el.getElementsByTagName("span")[0];
34981             return {"el": el, "inner": inner};
34982         //}
34983     }
34984         
34985     
34986 });
34987
34988 /**
34989  * @class Roo.TabPanelItem
34990  * @extends Roo.util.Observable
34991  * Represents an individual item (tab plus body) in a TabPanel.
34992  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34993  * @param {String} id The id of this TabPanelItem
34994  * @param {String} text The text for the tab of this TabPanelItem
34995  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34996  */
34997 Roo.bootstrap.panel.TabItem = function(config){
34998     /**
34999      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35000      * @type Roo.TabPanel
35001      */
35002     this.tabPanel = config.panel;
35003     /**
35004      * The id for this TabPanelItem
35005      * @type String
35006      */
35007     this.id = config.id;
35008     /** @private */
35009     this.disabled = false;
35010     /** @private */
35011     this.text = config.text;
35012     /** @private */
35013     this.loaded = false;
35014     this.closable = config.closable;
35015
35016     /**
35017      * The body element for this TabPanelItem.
35018      * @type Roo.Element
35019      */
35020     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35021     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35022     this.bodyEl.setStyle("display", "block");
35023     this.bodyEl.setStyle("zoom", "1");
35024     //this.hideAction();
35025
35026     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35027     /** @private */
35028     this.el = Roo.get(els.el);
35029     this.inner = Roo.get(els.inner, true);
35030     this.textEl = Roo.get(this.el.dom.firstChild, true);
35031     this.pnode = Roo.get(els.el.parentNode, true);
35032     this.el.on("mousedown", this.onTabMouseDown, this);
35033     this.el.on("click", this.onTabClick, this);
35034     /** @private */
35035     if(config.closable){
35036         var c = Roo.get(els.close, true);
35037         c.dom.title = this.closeText;
35038         c.addClassOnOver("close-over");
35039         c.on("click", this.closeClick, this);
35040      }
35041
35042     this.addEvents({
35043          /**
35044          * @event activate
35045          * Fires when this tab becomes the active tab.
35046          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35047          * @param {Roo.TabPanelItem} this
35048          */
35049         "activate": true,
35050         /**
35051          * @event beforeclose
35052          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35053          * @param {Roo.TabPanelItem} this
35054          * @param {Object} e Set cancel to true on this object to cancel the close.
35055          */
35056         "beforeclose": true,
35057         /**
35058          * @event close
35059          * Fires when this tab is closed.
35060          * @param {Roo.TabPanelItem} this
35061          */
35062          "close": true,
35063         /**
35064          * @event deactivate
35065          * Fires when this tab is no longer the active tab.
35066          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35067          * @param {Roo.TabPanelItem} this
35068          */
35069          "deactivate" : true
35070     });
35071     this.hidden = false;
35072
35073     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35074 };
35075
35076 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35077            {
35078     purgeListeners : function(){
35079        Roo.util.Observable.prototype.purgeListeners.call(this);
35080        this.el.removeAllListeners();
35081     },
35082     /**
35083      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35084      */
35085     show : function(){
35086         this.pnode.addClass("active");
35087         this.showAction();
35088         if(Roo.isOpera){
35089             this.tabPanel.stripWrap.repaint();
35090         }
35091         this.fireEvent("activate", this.tabPanel, this);
35092     },
35093
35094     /**
35095      * Returns true if this tab is the active tab.
35096      * @return {Boolean}
35097      */
35098     isActive : function(){
35099         return this.tabPanel.getActiveTab() == this;
35100     },
35101
35102     /**
35103      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35104      */
35105     hide : function(){
35106         this.pnode.removeClass("active");
35107         this.hideAction();
35108         this.fireEvent("deactivate", this.tabPanel, this);
35109     },
35110
35111     hideAction : function(){
35112         this.bodyEl.hide();
35113         this.bodyEl.setStyle("position", "absolute");
35114         this.bodyEl.setLeft("-20000px");
35115         this.bodyEl.setTop("-20000px");
35116     },
35117
35118     showAction : function(){
35119         this.bodyEl.setStyle("position", "relative");
35120         this.bodyEl.setTop("");
35121         this.bodyEl.setLeft("");
35122         this.bodyEl.show();
35123     },
35124
35125     /**
35126      * Set the tooltip for the tab.
35127      * @param {String} tooltip The tab's tooltip
35128      */
35129     setTooltip : function(text){
35130         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35131             this.textEl.dom.qtip = text;
35132             this.textEl.dom.removeAttribute('title');
35133         }else{
35134             this.textEl.dom.title = text;
35135         }
35136     },
35137
35138     onTabClick : function(e){
35139         e.preventDefault();
35140         this.tabPanel.activate(this.id);
35141     },
35142
35143     onTabMouseDown : function(e){
35144         e.preventDefault();
35145         this.tabPanel.activate(this.id);
35146     },
35147 /*
35148     getWidth : function(){
35149         return this.inner.getWidth();
35150     },
35151
35152     setWidth : function(width){
35153         var iwidth = width - this.pnode.getPadding("lr");
35154         this.inner.setWidth(iwidth);
35155         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35156         this.pnode.setWidth(width);
35157     },
35158 */
35159     /**
35160      * Show or hide the tab
35161      * @param {Boolean} hidden True to hide or false to show.
35162      */
35163     setHidden : function(hidden){
35164         this.hidden = hidden;
35165         this.pnode.setStyle("display", hidden ? "none" : "");
35166     },
35167
35168     /**
35169      * Returns true if this tab is "hidden"
35170      * @return {Boolean}
35171      */
35172     isHidden : function(){
35173         return this.hidden;
35174     },
35175
35176     /**
35177      * Returns the text for this tab
35178      * @return {String}
35179      */
35180     getText : function(){
35181         return this.text;
35182     },
35183     /*
35184     autoSize : function(){
35185         //this.el.beginMeasure();
35186         this.textEl.setWidth(1);
35187         /*
35188          *  #2804 [new] Tabs in Roojs
35189          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35190          */
35191         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35192         //this.el.endMeasure();
35193     //},
35194
35195     /**
35196      * Sets the text for the tab (Note: this also sets the tooltip text)
35197      * @param {String} text The tab's text and tooltip
35198      */
35199     setText : function(text){
35200         this.text = text;
35201         this.textEl.update(text);
35202         this.setTooltip(text);
35203         //if(!this.tabPanel.resizeTabs){
35204         //    this.autoSize();
35205         //}
35206     },
35207     /**
35208      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35209      */
35210     activate : function(){
35211         this.tabPanel.activate(this.id);
35212     },
35213
35214     /**
35215      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35216      */
35217     disable : function(){
35218         if(this.tabPanel.active != this){
35219             this.disabled = true;
35220             this.pnode.addClass("disabled");
35221         }
35222     },
35223
35224     /**
35225      * Enables this TabPanelItem if it was previously disabled.
35226      */
35227     enable : function(){
35228         this.disabled = false;
35229         this.pnode.removeClass("disabled");
35230     },
35231
35232     /**
35233      * Sets the content for this TabPanelItem.
35234      * @param {String} content The content
35235      * @param {Boolean} loadScripts true to look for and load scripts
35236      */
35237     setContent : function(content, loadScripts){
35238         this.bodyEl.update(content, loadScripts);
35239     },
35240
35241     /**
35242      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35243      * @return {Roo.UpdateManager} The UpdateManager
35244      */
35245     getUpdateManager : function(){
35246         return this.bodyEl.getUpdateManager();
35247     },
35248
35249     /**
35250      * Set a URL to be used to load the content for this TabPanelItem.
35251      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35252      * @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)
35253      * @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)
35254      * @return {Roo.UpdateManager} The UpdateManager
35255      */
35256     setUrl : function(url, params, loadOnce){
35257         if(this.refreshDelegate){
35258             this.un('activate', this.refreshDelegate);
35259         }
35260         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35261         this.on("activate", this.refreshDelegate);
35262         return this.bodyEl.getUpdateManager();
35263     },
35264
35265     /** @private */
35266     _handleRefresh : function(url, params, loadOnce){
35267         if(!loadOnce || !this.loaded){
35268             var updater = this.bodyEl.getUpdateManager();
35269             updater.update(url, params, this._setLoaded.createDelegate(this));
35270         }
35271     },
35272
35273     /**
35274      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35275      *   Will fail silently if the setUrl method has not been called.
35276      *   This does not activate the panel, just updates its content.
35277      */
35278     refresh : function(){
35279         if(this.refreshDelegate){
35280            this.loaded = false;
35281            this.refreshDelegate();
35282         }
35283     },
35284
35285     /** @private */
35286     _setLoaded : function(){
35287         this.loaded = true;
35288     },
35289
35290     /** @private */
35291     closeClick : function(e){
35292         var o = {};
35293         e.stopEvent();
35294         this.fireEvent("beforeclose", this, o);
35295         if(o.cancel !== true){
35296             this.tabPanel.removeTab(this.id);
35297         }
35298     },
35299     /**
35300      * The text displayed in the tooltip for the close icon.
35301      * @type String
35302      */
35303     closeText : "Close this tab"
35304 });