5f829f093e1516084fb70135eadb0512dc67e0cc
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default true
2460  * @cfg {String} size (sm|lg) default empty
2461  * 
2462  * 
2463  * @constructor
2464  * Create a new Modal Dialog
2465  * @param {Object} config The config object
2466  */
2467
2468 Roo.bootstrap.Modal = function(config){
2469     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470     this.addEvents({
2471         // raw events
2472         /**
2473          * @event btnclick
2474          * The raw btnclick event for the button
2475          * @param {Roo.EventObject} e
2476          */
2477         "btnclick" : true
2478     });
2479     this.buttons = this.buttons || [];
2480      
2481     if (this.tmpl) {
2482         this.tmpl = Roo.factory(this.tmpl);
2483     }
2484     
2485 };
2486
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2488     
2489     title : 'test dialog',
2490    
2491     buttons : false,
2492     
2493     // set on load...
2494      
2495     html: false,
2496     
2497     tmp: false,
2498     
2499     specificTitle: false,
2500     
2501     buttonPosition: 'right',
2502     
2503     allow_close : true,
2504     
2505     animate : true,
2506     
2507     fitwindow: false,
2508     
2509     
2510      // private
2511     dialogEl: false,
2512     bodyEl:  false,
2513     footerEl:  false,
2514     titleEl:  false,
2515     closeEl:  false,
2516     
2517     size: '',
2518     
2519     
2520     onRender : function(ct, position)
2521     {
2522         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2523      
2524         if(!this.el){
2525             var cfg = Roo.apply({},  this.getAutoCreate());
2526             cfg.id = Roo.id();
2527             //if(!cfg.name){
2528             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2529             //}
2530             //if (!cfg.name.length) {
2531             //    delete cfg.name;
2532            // }
2533             if (this.cls) {
2534                 cfg.cls += ' ' + this.cls;
2535             }
2536             if (this.style) {
2537                 cfg.style = this.style;
2538             }
2539             this.el = Roo.get(document.body).createChild(cfg, position);
2540         }
2541         //var type = this.el.dom.type;
2542         
2543         
2544         if(this.tabIndex !== undefined){
2545             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2546         }
2547         
2548         this.dialogEl = this.el.select('.modal-dialog',true).first();
2549         this.bodyEl = this.el.select('.modal-body',true).first();
2550         this.closeEl = this.el.select('.modal-header .close', true).first();
2551         this.footerEl = this.el.select('.modal-footer',true).first();
2552         this.titleEl = this.el.select('.modal-title',true).first();
2553         
2554         
2555          
2556         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557         this.maskEl.enableDisplayMode("block");
2558         this.maskEl.hide();
2559         //this.el.addClass("x-dlg-modal");
2560     
2561         if (this.buttons.length) {
2562             Roo.each(this.buttons, function(bb) {
2563                 var b = Roo.apply({}, bb);
2564                 b.xns = b.xns || Roo.bootstrap;
2565                 b.xtype = b.xtype || 'Button';
2566                 if (typeof(b.listeners) == 'undefined') {
2567                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2568                 }
2569                 
2570                 var btn = Roo.factory(b);
2571                 
2572                 btn.render(this.el.select('.modal-footer div').first());
2573                 
2574             },this);
2575         }
2576         // render the children.
2577         var nitems = [];
2578         
2579         if(typeof(this.items) != 'undefined'){
2580             var items = this.items;
2581             delete this.items;
2582
2583             for(var i =0;i < items.length;i++) {
2584                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2585             }
2586         }
2587         
2588         this.items = nitems;
2589         
2590         // where are these used - they used to be body/close/footer
2591         
2592        
2593         this.initEvents();
2594         //this.el.addClass([this.fieldClass, this.cls]);
2595         
2596     },
2597     
2598     getAutoCreate : function(){
2599         
2600         
2601         var bdy = {
2602                 cls : 'modal-body',
2603                 html : this.html || ''
2604         };
2605         
2606         var title = {
2607             tag: 'h4',
2608             cls : 'modal-title',
2609             html : this.title
2610         };
2611         
2612         if(this.specificTitle){
2613             title = this.title;
2614             
2615         };
2616         
2617         var header = [];
2618         if (this.allow_close) {
2619             header.push({
2620                 tag: 'button',
2621                 cls : 'close',
2622                 html : '&times'
2623             });
2624         }
2625         
2626         header.push(title);
2627         
2628         var size = '';
2629         
2630         if(this.size.length){
2631             size = 'modal-' + this.size;
2632         }
2633         
2634         var modal = {
2635             cls: "modal",
2636             style : 'display: none',
2637             cn : [
2638                 {
2639                     cls: "modal-dialog " + size,
2640                     cn : [
2641                         {
2642                             cls : "modal-content",
2643                             cn : [
2644                                 {
2645                                     cls : 'modal-header',
2646                                     cn : header
2647                                 },
2648                                 bdy,
2649                                 {
2650                                     cls : 'modal-footer',
2651                                     cn : [
2652                                         {
2653                                             tag: 'div',
2654                                             cls: 'btn-' + this.buttonPosition
2655                                         }
2656                                     ]
2657                                     
2658                                 }
2659                                 
2660                                 
2661                             ]
2662                             
2663                         }
2664                     ]
2665                         
2666                 }
2667             ]
2668         };
2669         
2670         if(this.animate){
2671             modal.cls += ' fade';
2672         }
2673         
2674         return modal;
2675           
2676     },
2677     getChildContainer : function() {
2678          
2679          return this.bodyEl;
2680         
2681     },
2682     getButtonContainer : function() {
2683          return this.el.select('.modal-footer div',true).first();
2684         
2685     },
2686     initEvents : function()
2687     {
2688         if (this.allow_close) {
2689             this.closeEl.on('click', this.hide, this);
2690         }
2691         Roo.EventManager.onWindowResize(this.resize, this, true);
2692         
2693  
2694     },
2695     
2696     resize : function()
2697     {
2698         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2699         if (this.fitwindow) {
2700             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2702             this.setSize(w,h)
2703         }
2704     },
2705     
2706     setSize : function(w,h)
2707     {
2708         if (!w && !h) {
2709             return;
2710         }
2711         this.resizeTo(w,h);
2712     },
2713     
2714     show : function() {
2715         
2716         if (!this.rendered) {
2717             this.render();
2718         }
2719         
2720         this.el.setStyle('display', 'block');
2721         
2722         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2723             var _this = this;
2724             (function(){
2725                 this.el.addClass('in');
2726             }).defer(50, this);
2727         }else{
2728             this.el.addClass('in');
2729             
2730         }
2731         
2732         // not sure how we can show data in here.. 
2733         //if (this.tmpl) {
2734         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2735         //}
2736         
2737         Roo.get(document.body).addClass("x-body-masked");
2738         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2739         this.maskEl.show();
2740         this.el.setStyle('zIndex', '10001');
2741        
2742         this.fireEvent('show', this);
2743         this.items.forEach(function(e) {
2744             e.layout ? e.layout() : false;
2745                 
2746         });
2747         this.resize();
2748         
2749         
2750         
2751     },
2752     hide : function()
2753     {
2754         this.maskEl.hide();
2755         Roo.get(document.body).removeClass("x-body-masked");
2756         this.el.removeClass('in');
2757         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2758         
2759         if(this.animate){ // why
2760             var _this = this;
2761             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2762         }else{
2763             this.el.setStyle('display', 'none');
2764         }
2765         
2766         this.fireEvent('hide', this);
2767     },
2768     
2769     addButton : function(str, cb)
2770     {
2771          
2772         
2773         var b = Roo.apply({}, { html : str } );
2774         b.xns = b.xns || Roo.bootstrap;
2775         b.xtype = b.xtype || 'Button';
2776         if (typeof(b.listeners) == 'undefined') {
2777             b.listeners = { click : cb.createDelegate(this)  };
2778         }
2779         
2780         var btn = Roo.factory(b);
2781            
2782         btn.render(this.el.select('.modal-footer div').first());
2783         
2784         return btn;   
2785        
2786     },
2787     
2788     setDefaultButton : function(btn)
2789     {
2790         //this.el.select('.modal-footer').()
2791     },
2792     diff : false,
2793     
2794     resizeTo: function(w,h)
2795     {
2796         // skip.. ?? why??
2797         
2798         this.dialogEl.setWidth(w);
2799         if (this.diff === false) {
2800             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2801         }
2802         
2803         this.bodyEl.setHeight(h-this.diff);
2804         
2805         
2806     },
2807     setContentSize  : function(w, h)
2808     {
2809         
2810     },
2811     onButtonClick: function(btn,e)
2812     {
2813         //Roo.log([a,b,c]);
2814         this.fireEvent('btnclick', btn.name, e);
2815     },
2816      /**
2817      * Set the title of the Dialog
2818      * @param {String} str new Title
2819      */
2820     setTitle: function(str) {
2821         this.titleEl.dom.innerHTML = str;    
2822     },
2823     /**
2824      * Set the body of the Dialog
2825      * @param {String} str new Title
2826      */
2827     setBody: function(str) {
2828         this.bodyEl.dom.innerHTML = str;    
2829     },
2830     /**
2831      * Set the body of the Dialog using the template
2832      * @param {Obj} data - apply this data to the template and replace the body contents.
2833      */
2834     applyBody: function(obj)
2835     {
2836         if (!this.tmpl) {
2837             Roo.log("Error - using apply Body without a template");
2838             //code
2839         }
2840         this.tmpl.overwrite(this.bodyEl, obj);
2841     }
2842     
2843 });
2844
2845
2846 Roo.apply(Roo.bootstrap.Modal,  {
2847     /**
2848          * Button config that displays a single OK button
2849          * @type Object
2850          */
2851         OK :  [{
2852             name : 'ok',
2853             weight : 'primary',
2854             html : 'OK'
2855         }], 
2856         /**
2857          * Button config that displays Yes and No buttons
2858          * @type Object
2859          */
2860         YESNO : [
2861             {
2862                 name  : 'no',
2863                 html : 'No'
2864             },
2865             {
2866                 name  :'yes',
2867                 weight : 'primary',
2868                 html : 'Yes'
2869             }
2870         ],
2871         
2872         /**
2873          * Button config that displays OK and Cancel buttons
2874          * @type Object
2875          */
2876         OKCANCEL : [
2877             {
2878                name : 'cancel',
2879                 html : 'Cancel'
2880             },
2881             {
2882                 name : 'ok',
2883                 weight : 'primary',
2884                 html : 'OK'
2885             }
2886         ],
2887         /**
2888          * Button config that displays Yes, No and Cancel buttons
2889          * @type Object
2890          */
2891         YESNOCANCEL : [
2892             {
2893                 name : 'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             },
2897             {
2898                 name : 'no',
2899                 html : 'No'
2900             },
2901             {
2902                 name : 'cancel',
2903                 html : 'Cancel'
2904             }
2905         ]
2906 });
2907  
2908  /*
2909  * - LGPL
2910  *
2911  * messagebox - can be used as a replace
2912  * 
2913  */
2914 /**
2915  * @class Roo.MessageBox
2916  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2917  * Example usage:
2918  *<pre><code>
2919 // Basic alert:
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2921
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2924     if (btn == 'ok'){
2925         // process text value...
2926     }
2927 });
2928
2929 // Show a dialog using config options:
2930 Roo.Msg.show({
2931    title:'Save Changes?',
2932    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933    buttons: Roo.Msg.YESNOCANCEL,
2934    fn: processResult,
2935    animEl: 'elId'
2936 });
2937 </code></pre>
2938  * @singleton
2939  */
2940 Roo.bootstrap.MessageBox = function(){
2941     var dlg, opt, mask, waitTimer;
2942     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943     var buttons, activeTextEl, bwidth;
2944
2945     
2946     // private
2947     var handleButton = function(button){
2948         dlg.hide();
2949         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2950     };
2951
2952     // private
2953     var handleHide = function(){
2954         if(opt && opt.cls){
2955             dlg.el.removeClass(opt.cls);
2956         }
2957         //if(waitTimer){
2958         //    Roo.TaskMgr.stop(waitTimer);
2959         //    waitTimer = null;
2960         //}
2961     };
2962
2963     // private
2964     var updateButtons = function(b){
2965         var width = 0;
2966         if(!b){
2967             buttons["ok"].hide();
2968             buttons["cancel"].hide();
2969             buttons["yes"].hide();
2970             buttons["no"].hide();
2971             //dlg.footer.dom.style.display = 'none';
2972             return width;
2973         }
2974         dlg.footerEl.dom.style.display = '';
2975         for(var k in buttons){
2976             if(typeof buttons[k] != "function"){
2977                 if(b[k]){
2978                     buttons[k].show();
2979                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980                     width += buttons[k].el.getWidth()+15;
2981                 }else{
2982                     buttons[k].hide();
2983                 }
2984             }
2985         }
2986         return width;
2987     };
2988
2989     // private
2990     var handleEsc = function(d, k, e){
2991         if(opt && opt.closable !== false){
2992             dlg.hide();
2993         }
2994         if(e){
2995             e.stopEvent();
2996         }
2997     };
2998
2999     return {
3000         /**
3001          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002          * @return {Roo.BasicDialog} The BasicDialog element
3003          */
3004         getDialog : function(){
3005            if(!dlg){
3006                 dlg = new Roo.bootstrap.Modal( {
3007                     //draggable: true,
3008                     //resizable:false,
3009                     //constraintoviewport:false,
3010                     //fixedcenter:true,
3011                     //collapsible : false,
3012                     //shim:true,
3013                     //modal: true,
3014                   //  width:400,
3015                   //  height:100,
3016                     //buttonAlign:"center",
3017                     closeClick : function(){
3018                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3019                             handleButton("no");
3020                         }else{
3021                             handleButton("cancel");
3022                         }
3023                     }
3024                 });
3025                 dlg.render();
3026                 dlg.on("hide", handleHide);
3027                 mask = dlg.mask;
3028                 //dlg.addKeyListener(27, handleEsc);
3029                 buttons = {};
3030                 this.buttons = buttons;
3031                 var bt = this.buttonText;
3032                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3036                 //Roo.log(buttons);
3037                 bodyEl = dlg.bodyEl.createChild({
3038
3039                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040                         '<textarea class="roo-mb-textarea"></textarea>' +
3041                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3042                 });
3043                 msgEl = bodyEl.dom.firstChild;
3044                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045                 textboxEl.enableDisplayMode();
3046                 textboxEl.addKeyListener([10,13], function(){
3047                     if(dlg.isVisible() && opt && opt.buttons){
3048                         if(opt.buttons.ok){
3049                             handleButton("ok");
3050                         }else if(opt.buttons.yes){
3051                             handleButton("yes");
3052                         }
3053                     }
3054                 });
3055                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056                 textareaEl.enableDisplayMode();
3057                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058                 progressEl.enableDisplayMode();
3059                 var pf = progressEl.dom.firstChild;
3060                 if (pf) {
3061                     pp = Roo.get(pf.firstChild);
3062                     pp.setHeight(pf.offsetHeight);
3063                 }
3064                 
3065             }
3066             return dlg;
3067         },
3068
3069         /**
3070          * Updates the message box body text
3071          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072          * the XHTML-compliant non-breaking space character '&amp;#160;')
3073          * @return {Roo.MessageBox} This message box
3074          */
3075         updateText : function(text){
3076             if(!dlg.isVisible() && !opt.width){
3077                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3078             }
3079             msgEl.innerHTML = text || '&#160;';
3080       
3081             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3083             var w = Math.max(
3084                     Math.min(opt.width || cw , this.maxWidth), 
3085                     Math.max(opt.minWidth || this.minWidth, bwidth)
3086             );
3087             if(opt.prompt){
3088                 activeTextEl.setWidth(w);
3089             }
3090             if(dlg.isVisible()){
3091                 dlg.fixedcenter = false;
3092             }
3093             // to big, make it scroll. = But as usual stupid IE does not support
3094             // !important..
3095             
3096             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3099             } else {
3100                 bodyEl.dom.style.height = '';
3101                 bodyEl.dom.style.overflowY = '';
3102             }
3103             if (cw > w) {
3104                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3105             } else {
3106                 bodyEl.dom.style.overflowX = '';
3107             }
3108             
3109             dlg.setContentSize(w, bodyEl.getHeight());
3110             if(dlg.isVisible()){
3111                 dlg.fixedcenter = true;
3112             }
3113             return this;
3114         },
3115
3116         /**
3117          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3118          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121          * @return {Roo.MessageBox} This message box
3122          */
3123         updateProgress : function(value, text){
3124             if(text){
3125                 this.updateText(text);
3126             }
3127             if (pp) { // weird bug on my firefox - for some reason this is not defined
3128                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3129             }
3130             return this;
3131         },        
3132
3133         /**
3134          * Returns true if the message box is currently displayed
3135          * @return {Boolean} True if the message box is visible, else false
3136          */
3137         isVisible : function(){
3138             return dlg && dlg.isVisible();  
3139         },
3140
3141         /**
3142          * Hides the message box if it is displayed
3143          */
3144         hide : function(){
3145             if(this.isVisible()){
3146                 dlg.hide();
3147             }  
3148         },
3149
3150         /**
3151          * Displays a new message box, or reinitializes an existing message box, based on the config options
3152          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153          * The following config object properties are supported:
3154          * <pre>
3155 Property    Type             Description
3156 ----------  ---------------  ------------------------------------------------------------------------------------
3157 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3158                                    closes (defaults to undefined)
3159 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3162                                    progress and wait dialogs will ignore this property and always hide the
3163                                    close button as they can only be closed programmatically.
3164 cls               String           A custom CSS class to apply to the message box element
3165 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3166                                    displayed (defaults to 75)
3167 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3168                                    function will be btn (the name of the button that was clicked, if applicable,
3169                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3170                                    Progress and wait dialogs will ignore this option since they do not respond to
3171                                    user actions and can only be closed programmatically, so any required function
3172                                    should be called by the same code after it closes the dialog.
3173 icon              String           A CSS class that provides a background image to be used as an icon for
3174                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3176 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3177 modal             Boolean          False to allow user interaction with the page while the message box is
3178                                    displayed (defaults to true)
3179 msg               String           A string that will replace the existing message box body text (defaults
3180                                    to the XHTML-compliant non-breaking space character '&#160;')
3181 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3182 progress          Boolean          True to display a progress bar (defaults to false)
3183 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3186 title             String           The title text
3187 value             String           The string value to set into the active textbox element if displayed
3188 wait              Boolean          True to display a progress bar (defaults to false)
3189 width             Number           The width of the dialog in pixels
3190 </pre>
3191          *
3192          * Example usage:
3193          * <pre><code>
3194 Roo.Msg.show({
3195    title: 'Address',
3196    msg: 'Please enter your address:',
3197    width: 300,
3198    buttons: Roo.MessageBox.OKCANCEL,
3199    multiline: true,
3200    fn: saveAddress,
3201    animEl: 'addAddressBtn'
3202 });
3203 </code></pre>
3204          * @param {Object} config Configuration options
3205          * @return {Roo.MessageBox} This message box
3206          */
3207         show : function(options)
3208         {
3209             
3210             // this causes nightmares if you show one dialog after another
3211             // especially on callbacks..
3212              
3213             if(this.isVisible()){
3214                 
3215                 this.hide();
3216                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3218                 Roo.log("New Dialog Message:" +  options.msg )
3219                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3221                 
3222             }
3223             var d = this.getDialog();
3224             opt = options;
3225             d.setTitle(opt.title || "&#160;");
3226             d.closeEl.setDisplayed(opt.closable !== false);
3227             activeTextEl = textboxEl;
3228             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3229             if(opt.prompt){
3230                 if(opt.multiline){
3231                     textboxEl.hide();
3232                     textareaEl.show();
3233                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3234                         opt.multiline : this.defaultTextHeight);
3235                     activeTextEl = textareaEl;
3236                 }else{
3237                     textboxEl.show();
3238                     textareaEl.hide();
3239                 }
3240             }else{
3241                 textboxEl.hide();
3242                 textareaEl.hide();
3243             }
3244             progressEl.setDisplayed(opt.progress === true);
3245             this.updateProgress(0);
3246             activeTextEl.dom.value = opt.value || "";
3247             if(opt.prompt){
3248                 dlg.setDefaultButton(activeTextEl);
3249             }else{
3250                 var bs = opt.buttons;
3251                 var db = null;
3252                 if(bs && bs.ok){
3253                     db = buttons["ok"];
3254                 }else if(bs && bs.yes){
3255                     db = buttons["yes"];
3256                 }
3257                 dlg.setDefaultButton(db);
3258             }
3259             bwidth = updateButtons(opt.buttons);
3260             this.updateText(opt.msg);
3261             if(opt.cls){
3262                 d.el.addClass(opt.cls);
3263             }
3264             d.proxyDrag = opt.proxyDrag === true;
3265             d.modal = opt.modal !== false;
3266             d.mask = opt.modal !== false ? mask : false;
3267             if(!d.isVisible()){
3268                 // force it to the end of the z-index stack so it gets a cursor in FF
3269                 document.body.appendChild(dlg.el.dom);
3270                 d.animateTarget = null;
3271                 d.show(options.animEl);
3272             }
3273             return this;
3274         },
3275
3276         /**
3277          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3278          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279          * and closing the message box when the process is complete.
3280          * @param {String} title The title bar text
3281          * @param {String} msg The message box body text
3282          * @return {Roo.MessageBox} This message box
3283          */
3284         progress : function(title, msg){
3285             this.show({
3286                 title : title,
3287                 msg : msg,
3288                 buttons: false,
3289                 progress:true,
3290                 closable:false,
3291                 minWidth: this.minProgressWidth,
3292                 modal : true
3293             });
3294             return this;
3295         },
3296
3297         /**
3298          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299          * If a callback function is passed it will be called after the user clicks the button, and the
3300          * id of the button that was clicked will be passed as the only parameter to the callback
3301          * (could also be the top-right close button).
3302          * @param {String} title The title bar text
3303          * @param {String} msg The message box body text
3304          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305          * @param {Object} scope (optional) The scope of the callback function
3306          * @return {Roo.MessageBox} This message box
3307          */
3308         alert : function(title, msg, fn, scope){
3309             this.show({
3310                 title : title,
3311                 msg : msg,
3312                 buttons: this.OK,
3313                 fn: fn,
3314                 scope : scope,
3315                 modal : true
3316             });
3317             return this;
3318         },
3319
3320         /**
3321          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3322          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323          * You are responsible for closing the message box when the process is complete.
3324          * @param {String} msg The message box body text
3325          * @param {String} title (optional) The title bar text
3326          * @return {Roo.MessageBox} This message box
3327          */
3328         wait : function(msg, title){
3329             this.show({
3330                 title : title,
3331                 msg : msg,
3332                 buttons: false,
3333                 closable:false,
3334                 progress:true,
3335                 modal:true,
3336                 width:300,
3337                 wait:true
3338             });
3339             waitTimer = Roo.TaskMgr.start({
3340                 run: function(i){
3341                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3342                 },
3343                 interval: 1000
3344             });
3345             return this;
3346         },
3347
3348         /**
3349          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352          * @param {String} title The title bar text
3353          * @param {String} msg The message box body text
3354          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355          * @param {Object} scope (optional) The scope of the callback function
3356          * @return {Roo.MessageBox} This message box
3357          */
3358         confirm : function(title, msg, fn, scope){
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.YESNO,
3363                 fn: fn,
3364                 scope : scope,
3365                 modal : true
3366             });
3367             return this;
3368         },
3369
3370         /**
3371          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3373          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374          * (could also be the top-right close button) and the text that was entered will be passed as the two
3375          * parameters to the callback.
3376          * @param {String} title The title bar text
3377          * @param {String} msg The message box body text
3378          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379          * @param {Object} scope (optional) The scope of the callback function
3380          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382          * @return {Roo.MessageBox} This message box
3383          */
3384         prompt : function(title, msg, fn, scope, multiline){
3385             this.show({
3386                 title : title,
3387                 msg : msg,
3388                 buttons: this.OKCANCEL,
3389                 fn: fn,
3390                 minWidth:250,
3391                 scope : scope,
3392                 prompt:true,
3393                 multiline: multiline,
3394                 modal : true
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Button config that displays a single OK button
3401          * @type Object
3402          */
3403         OK : {ok:true},
3404         /**
3405          * Button config that displays Yes and No buttons
3406          * @type Object
3407          */
3408         YESNO : {yes:true, no:true},
3409         /**
3410          * Button config that displays OK and Cancel buttons
3411          * @type Object
3412          */
3413         OKCANCEL : {ok:true, cancel:true},
3414         /**
3415          * Button config that displays Yes, No and Cancel buttons
3416          * @type Object
3417          */
3418         YESNOCANCEL : {yes:true, no:true, cancel:true},
3419
3420         /**
3421          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3422          * @type Number
3423          */
3424         defaultTextHeight : 75,
3425         /**
3426          * The maximum width in pixels of the message box (defaults to 600)
3427          * @type Number
3428          */
3429         maxWidth : 600,
3430         /**
3431          * The minimum width in pixels of the message box (defaults to 100)
3432          * @type Number
3433          */
3434         minWidth : 100,
3435         /**
3436          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3437          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3438          * @type Number
3439          */
3440         minProgressWidth : 250,
3441         /**
3442          * An object containing the default button text strings that can be overriden for localized language support.
3443          * Supported properties are: ok, cancel, yes and no.
3444          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3445          * @type Object
3446          */
3447         buttonText : {
3448             ok : "OK",
3449             cancel : "Cancel",
3450             yes : "Yes",
3451             no : "No"
3452         }
3453     };
3454 }();
3455
3456 /**
3457  * Shorthand for {@link Roo.MessageBox}
3458  */
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3461 /*
3462  * - LGPL
3463  *
3464  * navbar
3465  * 
3466  */
3467
3468 /**
3469  * @class Roo.bootstrap.Navbar
3470  * @extends Roo.bootstrap.Component
3471  * Bootstrap Navbar class
3472
3473  * @constructor
3474  * Create a new Navbar
3475  * @param {Object} config The config object
3476  */
3477
3478
3479 Roo.bootstrap.Navbar = function(config){
3480     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3481     this.addEvents({
3482         // raw events
3483         /**
3484          * @event beforetoggle
3485          * Fire before toggle the menu
3486          * @param {Roo.EventObject} e
3487          */
3488         "beforetoggle" : true
3489     });
3490 };
3491
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3493     
3494     
3495    
3496     // private
3497     navItems : false,
3498     loadMask : false,
3499     
3500     
3501     getAutoCreate : function(){
3502         
3503         
3504         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3505         
3506     },
3507     
3508     initEvents :function ()
3509     {
3510         //Roo.log(this.el.select('.navbar-toggle',true));
3511         this.el.select('.navbar-toggle',true).on('click', function() {
3512             if(this.fireEvent('beforetoggle', this) !== false){
3513                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3514             }
3515             
3516         }, this);
3517         
3518         var mark = {
3519             tag: "div",
3520             cls:"x-dlg-mask"
3521         };
3522         
3523         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3524         
3525         var size = this.el.getSize();
3526         this.maskEl.setSize(size.width, size.height);
3527         this.maskEl.enableDisplayMode("block");
3528         this.maskEl.hide();
3529         
3530         if(this.loadMask){
3531             this.maskEl.show();
3532         }
3533     },
3534     
3535     
3536     getChildContainer : function()
3537     {
3538         if (this.el.select('.collapse').getCount()) {
3539             return this.el.select('.collapse',true).first();
3540         }
3541         
3542         return this.el;
3543     },
3544     
3545     mask : function()
3546     {
3547         this.maskEl.show();
3548     },
3549     
3550     unmask : function()
3551     {
3552         this.maskEl.hide();
3553     } 
3554     
3555     
3556     
3557     
3558 });
3559
3560
3561
3562  
3563
3564  /*
3565  * - LGPL
3566  *
3567  * navbar
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.NavSimplebar
3573  * @extends Roo.bootstrap.Navbar
3574  * Bootstrap Sidebar class
3575  *
3576  * @cfg {Boolean} inverse is inverted color
3577  * 
3578  * @cfg {String} type (nav | pills | tabs)
3579  * @cfg {Boolean} arrangement stacked | justified
3580  * @cfg {String} align (left | right) alignment
3581  * 
3582  * @cfg {Boolean} main (true|false) main nav bar? default false
3583  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3584  * 
3585  * @cfg {String} tag (header|footer|nav|div) default is nav 
3586
3587  * 
3588  * 
3589  * 
3590  * @constructor
3591  * Create a new Sidebar
3592  * @param {Object} config The config object
3593  */
3594
3595
3596 Roo.bootstrap.NavSimplebar = function(config){
3597     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3598 };
3599
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3601     
3602     inverse: false,
3603     
3604     type: false,
3605     arrangement: '',
3606     align : false,
3607     
3608     
3609     
3610     main : false,
3611     
3612     
3613     tag : false,
3614     
3615     
3616     getAutoCreate : function(){
3617         
3618         
3619         var cfg = {
3620             tag : this.tag || 'div',
3621             cls : 'navbar'
3622         };
3623           
3624         
3625         cfg.cn = [
3626             {
3627                 cls: 'nav',
3628                 tag : 'ul'
3629             }
3630         ];
3631         
3632          
3633         this.type = this.type || 'nav';
3634         if (['tabs','pills'].indexOf(this.type)!==-1) {
3635             cfg.cn[0].cls += ' nav-' + this.type
3636         
3637         
3638         } else {
3639             if (this.type!=='nav') {
3640                 Roo.log('nav type must be nav/tabs/pills')
3641             }
3642             cfg.cn[0].cls += ' navbar-nav'
3643         }
3644         
3645         
3646         
3647         
3648         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649             cfg.cn[0].cls += ' nav-' + this.arrangement;
3650         }
3651         
3652         
3653         if (this.align === 'right') {
3654             cfg.cn[0].cls += ' navbar-right';
3655         }
3656         
3657         if (this.inverse) {
3658             cfg.cls += ' navbar-inverse';
3659             
3660         }
3661         
3662         
3663         return cfg;
3664     
3665         
3666     }
3667     
3668     
3669     
3670 });
3671
3672
3673
3674  
3675
3676  
3677        /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavHeaderbar
3686  * @extends Roo.bootstrap.NavSimplebar
3687  * Bootstrap Sidebar class
3688  *
3689  * @cfg {String} brand what is brand
3690  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691  * @cfg {String} brand_href href of the brand
3692  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3693  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3696  * 
3697  * @constructor
3698  * Create a new Sidebar
3699  * @param {Object} config The config object
3700  */
3701
3702
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3705       
3706 };
3707
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3709     
3710     position: '',
3711     brand: '',
3712     brand_href: false,
3713     srButton : true,
3714     autohide : false,
3715     desktopCenter : false,
3716    
3717     
3718     getAutoCreate : function(){
3719         
3720         var   cfg = {
3721             tag: this.nav || 'nav',
3722             cls: 'navbar',
3723             role: 'navigation',
3724             cn: []
3725         };
3726         
3727         var cn = cfg.cn;
3728         if (this.desktopCenter) {
3729             cn.push({cls : 'container', cn : []});
3730             cn = cn[0].cn;
3731         }
3732         
3733         if(this.srButton){
3734             cn.push({
3735                 tag: 'div',
3736                 cls: 'navbar-header',
3737                 cn: [
3738                     {
3739                         tag: 'button',
3740                         type: 'button',
3741                         cls: 'navbar-toggle',
3742                         'data-toggle': 'collapse',
3743                         cn: [
3744                             {
3745                                 tag: 'span',
3746                                 cls: 'sr-only',
3747                                 html: 'Toggle navigation'
3748                             },
3749                             {
3750                                 tag: 'span',
3751                                 cls: 'icon-bar'
3752                             },
3753                             {
3754                                 tag: 'span',
3755                                 cls: 'icon-bar'
3756                             },
3757                             {
3758                                 tag: 'span',
3759                                 cls: 'icon-bar'
3760                             }
3761                         ]
3762                     }
3763                 ]
3764             });
3765         }
3766         
3767         cn.push({
3768             tag: 'div',
3769             cls: 'collapse navbar-collapse',
3770             cn : []
3771         });
3772         
3773         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3774         
3775         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776             cfg.cls += ' navbar-' + this.position;
3777             
3778             // tag can override this..
3779             
3780             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3781         }
3782         
3783         if (this.brand !== '') {
3784             cn[0].cn.push({
3785                 tag: 'a',
3786                 href: this.brand_href ? this.brand_href : '#',
3787                 cls: 'navbar-brand',
3788                 cn: [
3789                 this.brand
3790                 ]
3791             });
3792         }
3793         
3794         if(this.main){
3795             cfg.cls += ' main-nav';
3796         }
3797         
3798         
3799         return cfg;
3800
3801         
3802     },
3803     getHeaderChildContainer : function()
3804     {
3805         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806             return this.el.select('.navbar-header',true).first();
3807         }
3808         
3809         return this.getChildContainer();
3810     },
3811     
3812     
3813     initEvents : function()
3814     {
3815         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3816         
3817         if (this.autohide) {
3818             
3819             var prevScroll = 0;
3820             var ft = this.el;
3821             
3822             Roo.get(document).on('scroll',function(e) {
3823                 var ns = Roo.get(document).getScroll().top;
3824                 var os = prevScroll;
3825                 prevScroll = ns;
3826                 
3827                 if(ns > os){
3828                     ft.removeClass('slideDown');
3829                     ft.addClass('slideUp');
3830                     return;
3831                 }
3832                 ft.removeClass('slideUp');
3833                 ft.addClass('slideDown');
3834                  
3835               
3836           },this);
3837         }
3838     }    
3839     
3840 });
3841
3842
3843
3844  
3845
3846  /*
3847  * - LGPL
3848  *
3849  * navbar
3850  * 
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.NavSidebar
3855  * @extends Roo.bootstrap.Navbar
3856  * Bootstrap Sidebar class
3857  * 
3858  * @constructor
3859  * Create a new Sidebar
3860  * @param {Object} config The config object
3861  */
3862
3863
3864 Roo.bootstrap.NavSidebar = function(config){
3865     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3866 };
3867
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3869     
3870     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3871     
3872     getAutoCreate : function(){
3873         
3874         
3875         return  {
3876             tag: 'div',
3877             cls: 'sidebar sidebar-nav'
3878         };
3879     
3880         
3881     }
3882     
3883     
3884     
3885 });
3886
3887
3888
3889  
3890
3891  /*
3892  * - LGPL
3893  *
3894  * nav group
3895  * 
3896  */
3897
3898 /**
3899  * @class Roo.bootstrap.NavGroup
3900  * @extends Roo.bootstrap.Component
3901  * Bootstrap NavGroup class
3902  * @cfg {String} align (left|right)
3903  * @cfg {Boolean} inverse
3904  * @cfg {String} type (nav|pills|tab) default nav
3905  * @cfg {String} navId - reference Id for navbar.
3906
3907  * 
3908  * @constructor
3909  * Create a new nav group
3910  * @param {Object} config The config object
3911  */
3912
3913 Roo.bootstrap.NavGroup = function(config){
3914     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3915     this.navItems = [];
3916    
3917     Roo.bootstrap.NavGroup.register(this);
3918      this.addEvents({
3919         /**
3920              * @event changed
3921              * Fires when the active item changes
3922              * @param {Roo.bootstrap.NavGroup} this
3923              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3925          */
3926         'changed': true
3927      });
3928     
3929 };
3930
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3932     
3933     align: '',
3934     inverse: false,
3935     form: false,
3936     type: 'nav',
3937     navId : '',
3938     // private
3939     
3940     navItems : false, 
3941     
3942     getAutoCreate : function()
3943     {
3944         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3945         
3946         cfg = {
3947             tag : 'ul',
3948             cls: 'nav' 
3949         };
3950         
3951         if (['tabs','pills'].indexOf(this.type)!==-1) {
3952             cfg.cls += ' nav-' + this.type
3953         } else {
3954             if (this.type!=='nav') {
3955                 Roo.log('nav type must be nav/tabs/pills')
3956             }
3957             cfg.cls += ' navbar-nav'
3958         }
3959         
3960         if (this.parent().sidebar) {
3961             cfg = {
3962                 tag: 'ul',
3963                 cls: 'dashboard-menu sidebar-menu'
3964             };
3965             
3966             return cfg;
3967         }
3968         
3969         if (this.form === true) {
3970             cfg = {
3971                 tag: 'form',
3972                 cls: 'navbar-form'
3973             };
3974             
3975             if (this.align === 'right') {
3976                 cfg.cls += ' navbar-right';
3977             } else {
3978                 cfg.cls += ' navbar-left';
3979             }
3980         }
3981         
3982         if (this.align === 'right') {
3983             cfg.cls += ' navbar-right';
3984         }
3985         
3986         if (this.inverse) {
3987             cfg.cls += ' navbar-inverse';
3988             
3989         }
3990         
3991         
3992         return cfg;
3993     },
3994     /**
3995     * sets the active Navigation item
3996     * @param {Roo.bootstrap.NavItem} the new current navitem
3997     */
3998     setActiveItem : function(item)
3999     {
4000         var prev = false;
4001         Roo.each(this.navItems, function(v){
4002             if (v == item) {
4003                 return ;
4004             }
4005             if (v.isActive()) {
4006                 v.setActive(false, true);
4007                 prev = v;
4008                 
4009             }
4010             
4011         });
4012
4013         item.setActive(true, true);
4014         this.fireEvent('changed', this, item, prev);
4015         
4016         
4017     },
4018     /**
4019     * gets the active Navigation item
4020     * @return {Roo.bootstrap.NavItem} the current navitem
4021     */
4022     getActive : function()
4023     {
4024         
4025         var prev = false;
4026         Roo.each(this.navItems, function(v){
4027             
4028             if (v.isActive()) {
4029                 prev = v;
4030                 
4031             }
4032             
4033         });
4034         return prev;
4035     },
4036     
4037     indexOfNav : function()
4038     {
4039         
4040         var prev = false;
4041         Roo.each(this.navItems, function(v,i){
4042             
4043             if (v.isActive()) {
4044                 prev = i;
4045                 
4046             }
4047             
4048         });
4049         return prev;
4050     },
4051     /**
4052     * adds a Navigation item
4053     * @param {Roo.bootstrap.NavItem} the navitem to add
4054     */
4055     addItem : function(cfg)
4056     {
4057         var cn = new Roo.bootstrap.NavItem(cfg);
4058         this.register(cn);
4059         cn.parentId = this.id;
4060         cn.onRender(this.el, null);
4061         return cn;
4062     },
4063     /**
4064     * register a Navigation item
4065     * @param {Roo.bootstrap.NavItem} the navitem to add
4066     */
4067     register : function(item)
4068     {
4069         this.navItems.push( item);
4070         item.navId = this.navId;
4071     
4072     },
4073     
4074     /**
4075     * clear all the Navigation item
4076     */
4077    
4078     clearAll : function()
4079     {
4080         this.navItems = [];
4081         this.el.dom.innerHTML = '';
4082     },
4083     
4084     getNavItem: function(tabId)
4085     {
4086         var ret = false;
4087         Roo.each(this.navItems, function(e) {
4088             if (e.tabId == tabId) {
4089                ret =  e;
4090                return false;
4091             }
4092             return true;
4093             
4094         });
4095         return ret;
4096     },
4097     
4098     setActiveNext : function()
4099     {
4100         var i = this.indexOfNav(this.getActive());
4101         if (i > this.navItems.length) {
4102             return;
4103         }
4104         this.setActiveItem(this.navItems[i+1]);
4105     },
4106     setActivePrev : function()
4107     {
4108         var i = this.indexOfNav(this.getActive());
4109         if (i  < 1) {
4110             return;
4111         }
4112         this.setActiveItem(this.navItems[i-1]);
4113     },
4114     clearWasActive : function(except) {
4115         Roo.each(this.navItems, function(e) {
4116             if (e.tabId != except.tabId && e.was_active) {
4117                e.was_active = false;
4118                return false;
4119             }
4120             return true;
4121             
4122         });
4123     },
4124     getWasActive : function ()
4125     {
4126         var r = false;
4127         Roo.each(this.navItems, function(e) {
4128             if (e.was_active) {
4129                r = e;
4130                return false;
4131             }
4132             return true;
4133             
4134         });
4135         return r;
4136     }
4137     
4138     
4139 });
4140
4141  
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4143     
4144     groups: {},
4145      /**
4146     * register a Navigation Group
4147     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4148     */
4149     register : function(navgrp)
4150     {
4151         this.groups[navgrp.navId] = navgrp;
4152         
4153     },
4154     /**
4155     * fetch a Navigation Group based on the navigation ID
4156     * @param {string} the navgroup to add
4157     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4158     */
4159     get: function(navId) {
4160         if (typeof(this.groups[navId]) == 'undefined') {
4161             return false;
4162             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4163         }
4164         return this.groups[navId] ;
4165     }
4166     
4167     
4168     
4169 });
4170
4171  /*
4172  * - LGPL
4173  *
4174  * row
4175  * 
4176  */
4177
4178 /**
4179  * @class Roo.bootstrap.NavItem
4180  * @extends Roo.bootstrap.Component
4181  * Bootstrap Navbar.NavItem class
4182  * @cfg {String} href  link to
4183  * @cfg {String} html content of button
4184  * @cfg {String} badge text inside badge
4185  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186  * @cfg {String} glyphicon name of glyphicon
4187  * @cfg {String} icon name of font awesome icon
4188  * @cfg {Boolean} active Is item active
4189  * @cfg {Boolean} disabled Is item disabled
4190  
4191  * @cfg {Boolean} preventDefault (true | false) default false
4192  * @cfg {String} tabId the tab that this item activates.
4193  * @cfg {String} tagtype (a|span) render as a href or span?
4194  * @cfg {Boolean} animateRef (true|false) link to element default false  
4195   
4196  * @constructor
4197  * Create a new Navbar Item
4198  * @param {Object} config The config object
4199  */
4200 Roo.bootstrap.NavItem = function(config){
4201     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * The raw click event for the entire grid.
4207          * @param {Roo.EventObject} e
4208          */
4209         "click" : true,
4210          /**
4211             * @event changed
4212             * Fires when the active item active state changes
4213             * @param {Roo.bootstrap.NavItem} this
4214             * @param {boolean} state the new state
4215              
4216          */
4217         'changed': true,
4218         /**
4219             * @event scrollto
4220             * Fires when scroll to element
4221             * @param {Roo.bootstrap.NavItem} this
4222             * @param {Object} options
4223             * @param {Roo.EventObject} e
4224              
4225          */
4226         'scrollto': true
4227     });
4228    
4229 };
4230
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4232     
4233     href: false,
4234     html: '',
4235     badge: '',
4236     icon: false,
4237     glyphicon: false,
4238     active: false,
4239     preventDefault : false,
4240     tabId : false,
4241     tagtype : 'a',
4242     disabled : false,
4243     animateRef : false,
4244     was_active : false,
4245     
4246     getAutoCreate : function(){
4247          
4248         var cfg = {
4249             tag: 'li',
4250             cls: 'nav-item'
4251             
4252         };
4253         
4254         if (this.active) {
4255             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4256         }
4257         if (this.disabled) {
4258             cfg.cls += ' disabled';
4259         }
4260         
4261         if (this.href || this.html || this.glyphicon || this.icon) {
4262             cfg.cn = [
4263                 {
4264                     tag: this.tagtype,
4265                     href : this.href || "#",
4266                     html: this.html || ''
4267                 }
4268             ];
4269             
4270             if (this.icon) {
4271                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4272             }
4273
4274             if(this.glyphicon) {
4275                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4276             }
4277             
4278             if (this.menu) {
4279                 
4280                 cfg.cn[0].html += " <span class='caret'></span>";
4281              
4282             }
4283             
4284             if (this.badge !== '') {
4285                  
4286                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4287             }
4288         }
4289         
4290         
4291         
4292         return cfg;
4293     },
4294     initEvents: function() 
4295     {
4296         if (typeof (this.menu) != 'undefined') {
4297             this.menu.parentType = this.xtype;
4298             this.menu.triggerEl = this.el;
4299             this.menu = this.addxtype(Roo.apply({}, this.menu));
4300         }
4301         
4302         this.el.select('a',true).on('click', this.onClick, this);
4303         
4304         if(this.tagtype == 'span'){
4305             this.el.select('span',true).on('click', this.onClick, this);
4306         }
4307        
4308         // at this point parent should be available..
4309         this.parent().register(this);
4310     },
4311     
4312     onClick : function(e)
4313     {
4314         if (e.getTarget('.dropdown-menu-item')) {
4315             // did you click on a menu itemm.... - then don't trigger onclick..
4316             return;
4317         }
4318         
4319         if(
4320                 this.preventDefault || 
4321                 this.href == '#' 
4322         ){
4323             Roo.log("NavItem - prevent Default?");
4324             e.preventDefault();
4325         }
4326         
4327         if (this.disabled) {
4328             return;
4329         }
4330         
4331         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332         if (tg && tg.transition) {
4333             Roo.log("waiting for the transitionend");
4334             return;
4335         }
4336         
4337         
4338         
4339         //Roo.log("fire event clicked");
4340         if(this.fireEvent('click', this, e) === false){
4341             return;
4342         };
4343         
4344         if(this.tagtype == 'span'){
4345             return;
4346         }
4347         
4348         //Roo.log(this.href);
4349         var ael = this.el.select('a',true).first();
4350         //Roo.log(ael);
4351         
4352         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355                 return; // ignore... - it's a 'hash' to another page.
4356             }
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359             this.scrollToElement(e);
4360         }
4361         
4362         
4363         var p =  this.parent();
4364    
4365         if (['tabs','pills'].indexOf(p.type)!==-1) {
4366             if (typeof(p.setActiveItem) !== 'undefined') {
4367                 p.setActiveItem(this);
4368             }
4369         }
4370         
4371         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373             // remove the collapsed menu expand...
4374             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4375         }
4376     },
4377     
4378     isActive: function () {
4379         return this.active
4380     },
4381     setActive : function(state, fire, is_was_active)
4382     {
4383         if (this.active && !state && this.navId) {
4384             this.was_active = true;
4385             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4386             if (nv) {
4387                 nv.clearWasActive(this);
4388             }
4389             
4390         }
4391         this.active = state;
4392         
4393         if (!state ) {
4394             this.el.removeClass('active');
4395         } else if (!this.el.hasClass('active')) {
4396             this.el.addClass('active');
4397         }
4398         if (fire) {
4399             this.fireEvent('changed', this, state);
4400         }
4401         
4402         // show a panel if it's registered and related..
4403         
4404         if (!this.navId || !this.tabId || !state || is_was_active) {
4405             return;
4406         }
4407         
4408         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4409         if (!tg) {
4410             return;
4411         }
4412         var pan = tg.getPanelByName(this.tabId);
4413         if (!pan) {
4414             return;
4415         }
4416         // if we can not flip to new panel - go back to old nav highlight..
4417         if (false == tg.showPanel(pan)) {
4418             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4419             if (nv) {
4420                 var onav = nv.getWasActive();
4421                 if (onav) {
4422                     onav.setActive(true, false, true);
4423                 }
4424             }
4425             
4426         }
4427         
4428         
4429         
4430     },
4431      // this should not be here...
4432     setDisabled : function(state)
4433     {
4434         this.disabled = state;
4435         if (!state ) {
4436             this.el.removeClass('disabled');
4437         } else if (!this.el.hasClass('disabled')) {
4438             this.el.addClass('disabled');
4439         }
4440         
4441     },
4442     
4443     /**
4444      * Fetch the element to display the tooltip on.
4445      * @return {Roo.Element} defaults to this.el
4446      */
4447     tooltipEl : function()
4448     {
4449         return this.el.select('' + this.tagtype + '', true).first();
4450     },
4451     
4452     scrollToElement : function(e)
4453     {
4454         var c = document.body;
4455         
4456         /*
4457          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4458          */
4459         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460             c = document.documentElement;
4461         }
4462         
4463         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4464         
4465         if(!target){
4466             return;
4467         }
4468
4469         var o = target.calcOffsetsTo(c);
4470         
4471         var options = {
4472             target : target,
4473             value : o[1]
4474         };
4475         
4476         this.fireEvent('scrollto', this, options, e);
4477         
4478         Roo.get(c).scrollTo('top', options.value, true);
4479         
4480         return;
4481     }
4482 });
4483  
4484
4485  /*
4486  * - LGPL
4487  *
4488  * sidebar item
4489  *
4490  *  li
4491  *    <span> icon </span>
4492  *    <span> text </span>
4493  *    <span>badge </span>
4494  */
4495
4496 /**
4497  * @class Roo.bootstrap.NavSidebarItem
4498  * @extends Roo.bootstrap.NavItem
4499  * Bootstrap Navbar.NavSidebarItem class
4500  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501  * {bool} open is the menu open
4502  * @constructor
4503  * Create a new Navbar Button
4504  * @param {Object} config The config object
4505  */
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4508     this.addEvents({
4509         // raw events
4510         /**
4511          * @event click
4512          * The raw click event for the entire grid.
4513          * @param {Roo.EventObject} e
4514          */
4515         "click" : true,
4516          /**
4517             * @event changed
4518             * Fires when the active item active state changes
4519             * @param {Roo.bootstrap.NavSidebarItem} this
4520             * @param {boolean} state the new state
4521              
4522          */
4523         'changed': true
4524     });
4525    
4526 };
4527
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4529     
4530     badgeWeight : 'default',
4531     
4532     open: false,
4533     
4534     getAutoCreate : function(){
4535         
4536         
4537         var a = {
4538                 tag: 'a',
4539                 href : this.href || '#',
4540                 cls: '',
4541                 html : '',
4542                 cn : []
4543         };
4544         var cfg = {
4545             tag: 'li',
4546             cls: '',
4547             cn: [ a ]
4548         };
4549         var span = {
4550             tag: 'span',
4551             html : this.html || ''
4552         };
4553         
4554         
4555         if (this.active) {
4556             cfg.cls += ' active';
4557         }
4558         
4559         if (this.disabled) {
4560             cfg.cls += ' disabled';
4561         }
4562         if (this.open) {
4563             cfg.cls += ' open x-open';
4564         }
4565         // left icon..
4566         if (this.glyphicon || this.icon) {
4567             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4568             a.cn.push({ tag : 'i', cls : c }) ;
4569         }
4570         // html..
4571         a.cn.push(span);
4572         // then badge..
4573         if (this.badge !== '') {
4574             
4575             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4576         }
4577         // fi
4578         if (this.menu) {
4579             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580             a.cls += 'dropdown-toggle treeview' ;
4581         }
4582         
4583         return cfg;
4584          
4585            
4586     },
4587     
4588     initEvents : function()
4589     { 
4590         if (typeof (this.menu) != 'undefined') {
4591             this.menu.parentType = this.xtype;
4592             this.menu.triggerEl = this.el;
4593             this.menu = this.addxtype(Roo.apply({}, this.menu));
4594         }
4595         
4596         this.el.on('click', this.onClick, this);
4597        
4598     
4599         if(this.badge !== ''){
4600  
4601             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4602         }
4603         
4604     },
4605     
4606     onClick : function(e)
4607     {
4608         if(this.disabled){
4609             e.preventDefault();
4610             return;
4611         }
4612         
4613         if(this.preventDefault){
4614             e.preventDefault();
4615         }
4616         
4617         this.fireEvent('click', this);
4618     },
4619     
4620     disable : function()
4621     {
4622         this.setDisabled(true);
4623     },
4624     
4625     enable : function()
4626     {
4627         this.setDisabled(false);
4628     },
4629     
4630     setDisabled : function(state)
4631     {
4632         if(this.disabled == state){
4633             return;
4634         }
4635         
4636         this.disabled = state;
4637         
4638         if (state) {
4639             this.el.addClass('disabled');
4640             return;
4641         }
4642         
4643         this.el.removeClass('disabled');
4644         
4645         return;
4646     },
4647     
4648     setActive : function(state)
4649     {
4650         if(this.active == state){
4651             return;
4652         }
4653         
4654         this.active = state;
4655         
4656         if (state) {
4657             this.el.addClass('active');
4658             return;
4659         }
4660         
4661         this.el.removeClass('active');
4662         
4663         return;
4664     },
4665     
4666     isActive: function () 
4667     {
4668         return this.active;
4669     },
4670     
4671     setBadge : function(str)
4672     {
4673         if(!this.badgeEl){
4674             return;
4675         }
4676         
4677         this.badgeEl.dom.innerHTML = str;
4678     }
4679     
4680    
4681      
4682  
4683 });
4684  
4685
4686  /*
4687  * - LGPL
4688  *
4689  * row
4690  * 
4691  */
4692
4693 /**
4694  * @class Roo.bootstrap.Row
4695  * @extends Roo.bootstrap.Component
4696  * Bootstrap Row class (contains columns...)
4697  * 
4698  * @constructor
4699  * Create a new Row
4700  * @param {Object} config The config object
4701  */
4702
4703 Roo.bootstrap.Row = function(config){
4704     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4705 };
4706
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4708     
4709     getAutoCreate : function(){
4710        return {
4711             cls: 'row clearfix'
4712        };
4713     }
4714     
4715     
4716 });
4717
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * element
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Element
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Element class
4731  * @cfg {String} html contents of the element
4732  * @cfg {String} tag tag of the element
4733  * @cfg {String} cls class of the element
4734  * @cfg {Boolean} preventDefault (true|false) default false
4735  * @cfg {Boolean} clickable (true|false) default false
4736  * 
4737  * @constructor
4738  * Create a new Element
4739  * @param {Object} config The config object
4740  */
4741
4742 Roo.bootstrap.Element = function(config){
4743     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4744     
4745     this.addEvents({
4746         // raw events
4747         /**
4748          * @event click
4749          * When a element is chick
4750          * @param {Roo.bootstrap.Element} this
4751          * @param {Roo.EventObject} e
4752          */
4753         "click" : true
4754     });
4755 };
4756
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4758     
4759     tag: 'div',
4760     cls: '',
4761     html: '',
4762     preventDefault: false, 
4763     clickable: false,
4764     
4765     getAutoCreate : function(){
4766         
4767         var cfg = {
4768             tag: this.tag,
4769             cls: this.cls,
4770             html: this.html
4771         };
4772         
4773         return cfg;
4774     },
4775     
4776     initEvents: function() 
4777     {
4778         Roo.bootstrap.Element.superclass.initEvents.call(this);
4779         
4780         if(this.clickable){
4781             this.el.on('click', this.onClick, this);
4782         }
4783         
4784     },
4785     
4786     onClick : function(e)
4787     {
4788         if(this.preventDefault){
4789             e.preventDefault();
4790         }
4791         
4792         this.fireEvent('click', this, e);
4793     },
4794     
4795     getValue : function()
4796     {
4797         return this.el.dom.innerHTML;
4798     },
4799     
4800     setValue : function(value)
4801     {
4802         this.el.dom.innerHTML = value;
4803     }
4804    
4805 });
4806
4807  
4808
4809  /*
4810  * - LGPL
4811  *
4812  * pagination
4813  * 
4814  */
4815
4816 /**
4817  * @class Roo.bootstrap.Pagination
4818  * @extends Roo.bootstrap.Component
4819  * Bootstrap Pagination class
4820  * @cfg {String} size xs | sm | md | lg
4821  * @cfg {Boolean} inverse false | true
4822  * 
4823  * @constructor
4824  * Create a new Pagination
4825  * @param {Object} config The config object
4826  */
4827
4828 Roo.bootstrap.Pagination = function(config){
4829     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4830 };
4831
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4833     
4834     cls: false,
4835     size: false,
4836     inverse: false,
4837     
4838     getAutoCreate : function(){
4839         var cfg = {
4840             tag: 'ul',
4841                 cls: 'pagination'
4842         };
4843         if (this.inverse) {
4844             cfg.cls += ' inverse';
4845         }
4846         if (this.html) {
4847             cfg.html=this.html;
4848         }
4849         if (this.cls) {
4850             cfg.cls += " " + this.cls;
4851         }
4852         return cfg;
4853     }
4854    
4855 });
4856
4857  
4858
4859  /*
4860  * - LGPL
4861  *
4862  * Pagination item
4863  * 
4864  */
4865
4866
4867 /**
4868  * @class Roo.bootstrap.PaginationItem
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap PaginationItem class
4871  * @cfg {String} html text
4872  * @cfg {String} href the link
4873  * @cfg {Boolean} preventDefault (true | false) default true
4874  * @cfg {Boolean} active (true | false) default false
4875  * @cfg {Boolean} disabled default false
4876  * 
4877  * 
4878  * @constructor
4879  * Create a new PaginationItem
4880  * @param {Object} config The config object
4881  */
4882
4883
4884 Roo.bootstrap.PaginationItem = function(config){
4885     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4886     this.addEvents({
4887         // raw events
4888         /**
4889          * @event click
4890          * The raw click event for the entire grid.
4891          * @param {Roo.EventObject} e
4892          */
4893         "click" : true
4894     });
4895 };
4896
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4898     
4899     href : false,
4900     html : false,
4901     preventDefault: true,
4902     active : false,
4903     cls : false,
4904     disabled: false,
4905     
4906     getAutoCreate : function(){
4907         var cfg= {
4908             tag: 'li',
4909             cn: [
4910                 {
4911                     tag : 'a',
4912                     href : this.href ? this.href : '#',
4913                     html : this.html ? this.html : ''
4914                 }
4915             ]
4916         };
4917         
4918         if(this.cls){
4919             cfg.cls = this.cls;
4920         }
4921         
4922         if(this.disabled){
4923             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4924         }
4925         
4926         if(this.active){
4927             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4928         }
4929         
4930         return cfg;
4931     },
4932     
4933     initEvents: function() {
4934         
4935         this.el.on('click', this.onClick, this);
4936         
4937     },
4938     onClick : function(e)
4939     {
4940         Roo.log('PaginationItem on click ');
4941         if(this.preventDefault){
4942             e.preventDefault();
4943         }
4944         
4945         if(this.disabled){
4946             return;
4947         }
4948         
4949         this.fireEvent('click', this, e);
4950     }
4951    
4952 });
4953
4954  
4955
4956  /*
4957  * - LGPL
4958  *
4959  * slider
4960  * 
4961  */
4962
4963
4964 /**
4965  * @class Roo.bootstrap.Slider
4966  * @extends Roo.bootstrap.Component
4967  * Bootstrap Slider class
4968  *    
4969  * @constructor
4970  * Create a new Slider
4971  * @param {Object} config The config object
4972  */
4973
4974 Roo.bootstrap.Slider = function(config){
4975     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4976 };
4977
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4979     
4980     getAutoCreate : function(){
4981         
4982         var cfg = {
4983             tag: 'div',
4984             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4985             cn: [
4986                 {
4987                     tag: 'a',
4988                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4989                 }
4990             ]
4991         };
4992         
4993         return cfg;
4994     }
4995    
4996 });
4997
4998  /*
4999  * Based on:
5000  * Ext JS Library 1.1.1
5001  * Copyright(c) 2006-2007, Ext JS, LLC.
5002  *
5003  * Originally Released Under LGPL - original licence link has changed is not relivant.
5004  *
5005  * Fork - LGPL
5006  * <script type="text/javascript">
5007  */
5008  
5009
5010 /**
5011  * @class Roo.grid.ColumnModel
5012  * @extends Roo.util.Observable
5013  * This is the default implementation of a ColumnModel used by the Grid. It defines
5014  * the columns in the grid.
5015  * <br>Usage:<br>
5016  <pre><code>
5017  var colModel = new Roo.grid.ColumnModel([
5018         {header: "Ticker", width: 60, sortable: true, locked: true},
5019         {header: "Company Name", width: 150, sortable: true},
5020         {header: "Market Cap.", width: 100, sortable: true},
5021         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022         {header: "Employees", width: 100, sortable: true, resizable: false}
5023  ]);
5024  </code></pre>
5025  * <p>
5026  
5027  * The config options listed for this class are options which may appear in each
5028  * individual column definition.
5029  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5030  * @constructor
5031  * @param {Object} config An Array of column config objects. See this class's
5032  * config objects for details.
5033 */
5034 Roo.grid.ColumnModel = function(config){
5035         /**
5036      * The config passed into the constructor
5037      */
5038     this.config = config;
5039     this.lookup = {};
5040
5041     // if no id, create one
5042     // if the column does not have a dataIndex mapping,
5043     // map it to the order it is in the config
5044     for(var i = 0, len = config.length; i < len; i++){
5045         var c = config[i];
5046         if(typeof c.dataIndex == "undefined"){
5047             c.dataIndex = i;
5048         }
5049         if(typeof c.renderer == "string"){
5050             c.renderer = Roo.util.Format[c.renderer];
5051         }
5052         if(typeof c.id == "undefined"){
5053             c.id = Roo.id();
5054         }
5055         if(c.editor && c.editor.xtype){
5056             c.editor  = Roo.factory(c.editor, Roo.grid);
5057         }
5058         if(c.editor && c.editor.isFormField){
5059             c.editor = new Roo.grid.GridEditor(c.editor);
5060         }
5061         this.lookup[c.id] = c;
5062     }
5063
5064     /**
5065      * The width of columns which have no width specified (defaults to 100)
5066      * @type Number
5067      */
5068     this.defaultWidth = 100;
5069
5070     /**
5071      * Default sortable of columns which have no sortable specified (defaults to false)
5072      * @type Boolean
5073      */
5074     this.defaultSortable = false;
5075
5076     this.addEvents({
5077         /**
5078              * @event widthchange
5079              * Fires when the width of a column changes.
5080              * @param {ColumnModel} this
5081              * @param {Number} columnIndex The column index
5082              * @param {Number} newWidth The new width
5083              */
5084             "widthchange": true,
5085         /**
5086              * @event headerchange
5087              * Fires when the text of a header changes.
5088              * @param {ColumnModel} this
5089              * @param {Number} columnIndex The column index
5090              * @param {Number} newText The new header text
5091              */
5092             "headerchange": true,
5093         /**
5094              * @event hiddenchange
5095              * Fires when a column is hidden or "unhidden".
5096              * @param {ColumnModel} this
5097              * @param {Number} columnIndex The column index
5098              * @param {Boolean} hidden true if hidden, false otherwise
5099              */
5100             "hiddenchange": true,
5101             /**
5102          * @event columnmoved
5103          * Fires when a column is moved.
5104          * @param {ColumnModel} this
5105          * @param {Number} oldIndex
5106          * @param {Number} newIndex
5107          */
5108         "columnmoved" : true,
5109         /**
5110          * @event columlockchange
5111          * Fires when a column's locked state is changed
5112          * @param {ColumnModel} this
5113          * @param {Number} colIndex
5114          * @param {Boolean} locked true if locked
5115          */
5116         "columnlockchange" : true
5117     });
5118     Roo.grid.ColumnModel.superclass.constructor.call(this);
5119 };
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5121     /**
5122      * @cfg {String} header The header text to display in the Grid view.
5123      */
5124     /**
5125      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127      * specified, the column's index is used as an index into the Record's data Array.
5128      */
5129     /**
5130      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5132      */
5133     /**
5134      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135      * Defaults to the value of the {@link #defaultSortable} property.
5136      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5137      */
5138     /**
5139      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5140      */
5141     /**
5142      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5143      */
5144     /**
5145      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5146      */
5147     /**
5148      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5149      */
5150     /**
5151      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5155      */
5156        /**
5157      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5158      */
5159     /**
5160      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5161      */
5162     /**
5163      * @cfg {String} cursor (Optional)
5164      */
5165     /**
5166      * @cfg {String} tooltip (Optional)
5167      */
5168     /**
5169      * @cfg {Number} xs (Optional)
5170      */
5171     /**
5172      * @cfg {Number} sm (Optional)
5173      */
5174     /**
5175      * @cfg {Number} md (Optional)
5176      */
5177     /**
5178      * @cfg {Number} lg (Optional)
5179      */
5180     /**
5181      * Returns the id of the column at the specified index.
5182      * @param {Number} index The column index
5183      * @return {String} the id
5184      */
5185     getColumnId : function(index){
5186         return this.config[index].id;
5187     },
5188
5189     /**
5190      * Returns the column for a specified id.
5191      * @param {String} id The column id
5192      * @return {Object} the column
5193      */
5194     getColumnById : function(id){
5195         return this.lookup[id];
5196     },
5197
5198     
5199     /**
5200      * Returns the column for a specified dataIndex.
5201      * @param {String} dataIndex The column dataIndex
5202      * @return {Object|Boolean} the column or false if not found
5203      */
5204     getColumnByDataIndex: function(dataIndex){
5205         var index = this.findColumnIndex(dataIndex);
5206         return index > -1 ? this.config[index] : false;
5207     },
5208     
5209     /**
5210      * Returns the index for a specified column id.
5211      * @param {String} id The column id
5212      * @return {Number} the index, or -1 if not found
5213      */
5214     getIndexById : function(id){
5215         for(var i = 0, len = this.config.length; i < len; i++){
5216             if(this.config[i].id == id){
5217                 return i;
5218             }
5219         }
5220         return -1;
5221     },
5222     
5223     /**
5224      * Returns the index for a specified column dataIndex.
5225      * @param {String} dataIndex The column dataIndex
5226      * @return {Number} the index, or -1 if not found
5227      */
5228     
5229     findColumnIndex : function(dataIndex){
5230         for(var i = 0, len = this.config.length; i < len; i++){
5231             if(this.config[i].dataIndex == dataIndex){
5232                 return i;
5233             }
5234         }
5235         return -1;
5236     },
5237     
5238     
5239     moveColumn : function(oldIndex, newIndex){
5240         var c = this.config[oldIndex];
5241         this.config.splice(oldIndex, 1);
5242         this.config.splice(newIndex, 0, c);
5243         this.dataMap = null;
5244         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5245     },
5246
5247     isLocked : function(colIndex){
5248         return this.config[colIndex].locked === true;
5249     },
5250
5251     setLocked : function(colIndex, value, suppressEvent){
5252         if(this.isLocked(colIndex) == value){
5253             return;
5254         }
5255         this.config[colIndex].locked = value;
5256         if(!suppressEvent){
5257             this.fireEvent("columnlockchange", this, colIndex, value);
5258         }
5259     },
5260
5261     getTotalLockedWidth : function(){
5262         var totalWidth = 0;
5263         for(var i = 0; i < this.config.length; i++){
5264             if(this.isLocked(i) && !this.isHidden(i)){
5265                 this.totalWidth += this.getColumnWidth(i);
5266             }
5267         }
5268         return totalWidth;
5269     },
5270
5271     getLockedCount : function(){
5272         for(var i = 0, len = this.config.length; i < len; i++){
5273             if(!this.isLocked(i)){
5274                 return i;
5275             }
5276         }
5277         
5278         return this.config.length;
5279     },
5280
5281     /**
5282      * Returns the number of columns.
5283      * @return {Number}
5284      */
5285     getColumnCount : function(visibleOnly){
5286         if(visibleOnly === true){
5287             var c = 0;
5288             for(var i = 0, len = this.config.length; i < len; i++){
5289                 if(!this.isHidden(i)){
5290                     c++;
5291                 }
5292             }
5293             return c;
5294         }
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300      * @param {Function} fn
5301      * @param {Object} scope (optional)
5302      * @return {Array} result
5303      */
5304     getColumnsBy : function(fn, scope){
5305         var r = [];
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             var c = this.config[i];
5308             if(fn.call(scope||this, c, i) === true){
5309                 r[r.length] = c;
5310             }
5311         }
5312         return r;
5313     },
5314
5315     /**
5316      * Returns true if the specified column is sortable.
5317      * @param {Number} col The column index
5318      * @return {Boolean}
5319      */
5320     isSortable : function(col){
5321         if(typeof this.config[col].sortable == "undefined"){
5322             return this.defaultSortable;
5323         }
5324         return this.config[col].sortable;
5325     },
5326
5327     /**
5328      * Returns the rendering (formatting) function defined for the column.
5329      * @param {Number} col The column index.
5330      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5331      */
5332     getRenderer : function(col){
5333         if(!this.config[col].renderer){
5334             return Roo.grid.ColumnModel.defaultRenderer;
5335         }
5336         return this.config[col].renderer;
5337     },
5338
5339     /**
5340      * Sets the rendering (formatting) function for a column.
5341      * @param {Number} col The column index
5342      * @param {Function} fn The function to use to process the cell's raw data
5343      * to return HTML markup for the grid view. The render function is called with
5344      * the following parameters:<ul>
5345      * <li>Data value.</li>
5346      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347      * <li>css A CSS style string to apply to the table cell.</li>
5348      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350      * <li>Row index</li>
5351      * <li>Column index</li>
5352      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5353      */
5354     setRenderer : function(col, fn){
5355         this.config[col].renderer = fn;
5356     },
5357
5358     /**
5359      * Returns the width for the specified column.
5360      * @param {Number} col The column index
5361      * @return {Number}
5362      */
5363     getColumnWidth : function(col){
5364         return this.config[col].width * 1 || this.defaultWidth;
5365     },
5366
5367     /**
5368      * Sets the width for a column.
5369      * @param {Number} col The column index
5370      * @param {Number} width The new width
5371      */
5372     setColumnWidth : function(col, width, suppressEvent){
5373         this.config[col].width = width;
5374         this.totalWidth = null;
5375         if(!suppressEvent){
5376              this.fireEvent("widthchange", this, col, width);
5377         }
5378     },
5379
5380     /**
5381      * Returns the total width of all columns.
5382      * @param {Boolean} includeHidden True to include hidden column widths
5383      * @return {Number}
5384      */
5385     getTotalWidth : function(includeHidden){
5386         if(!this.totalWidth){
5387             this.totalWidth = 0;
5388             for(var i = 0, len = this.config.length; i < len; i++){
5389                 if(includeHidden || !this.isHidden(i)){
5390                     this.totalWidth += this.getColumnWidth(i);
5391                 }
5392             }
5393         }
5394         return this.totalWidth;
5395     },
5396
5397     /**
5398      * Returns the header for the specified column.
5399      * @param {Number} col The column index
5400      * @return {String}
5401      */
5402     getColumnHeader : function(col){
5403         return this.config[col].header;
5404     },
5405
5406     /**
5407      * Sets the header for a column.
5408      * @param {Number} col The column index
5409      * @param {String} header The new header
5410      */
5411     setColumnHeader : function(col, header){
5412         this.config[col].header = header;
5413         this.fireEvent("headerchange", this, col, header);
5414     },
5415
5416     /**
5417      * Returns the tooltip for the specified column.
5418      * @param {Number} col The column index
5419      * @return {String}
5420      */
5421     getColumnTooltip : function(col){
5422             return this.config[col].tooltip;
5423     },
5424     /**
5425      * Sets the tooltip for a column.
5426      * @param {Number} col The column index
5427      * @param {String} tooltip The new tooltip
5428      */
5429     setColumnTooltip : function(col, tooltip){
5430             this.config[col].tooltip = tooltip;
5431     },
5432
5433     /**
5434      * Returns the dataIndex for the specified column.
5435      * @param {Number} col The column index
5436      * @return {Number}
5437      */
5438     getDataIndex : function(col){
5439         return this.config[col].dataIndex;
5440     },
5441
5442     /**
5443      * Sets the dataIndex for a column.
5444      * @param {Number} col The column index
5445      * @param {Number} dataIndex The new dataIndex
5446      */
5447     setDataIndex : function(col, dataIndex){
5448         this.config[col].dataIndex = dataIndex;
5449     },
5450
5451     
5452     
5453     /**
5454      * Returns true if the cell is editable.
5455      * @param {Number} colIndex The column index
5456      * @param {Number} rowIndex The row index - this is nto actually used..?
5457      * @return {Boolean}
5458      */
5459     isCellEditable : function(colIndex, rowIndex){
5460         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5461     },
5462
5463     /**
5464      * Returns the editor defined for the cell/column.
5465      * return false or null to disable editing.
5466      * @param {Number} colIndex The column index
5467      * @param {Number} rowIndex The row index
5468      * @return {Object}
5469      */
5470     getCellEditor : function(colIndex, rowIndex){
5471         return this.config[colIndex].editor;
5472     },
5473
5474     /**
5475      * Sets if a column is editable.
5476      * @param {Number} col The column index
5477      * @param {Boolean} editable True if the column is editable
5478      */
5479     setEditable : function(col, editable){
5480         this.config[col].editable = editable;
5481     },
5482
5483
5484     /**
5485      * Returns true if the column is hidden.
5486      * @param {Number} colIndex The column index
5487      * @return {Boolean}
5488      */
5489     isHidden : function(colIndex){
5490         return this.config[colIndex].hidden;
5491     },
5492
5493
5494     /**
5495      * Returns true if the column width cannot be changed
5496      */
5497     isFixed : function(colIndex){
5498         return this.config[colIndex].fixed;
5499     },
5500
5501     /**
5502      * Returns true if the column can be resized
5503      * @return {Boolean}
5504      */
5505     isResizable : function(colIndex){
5506         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5507     },
5508     /**
5509      * Sets if a column is hidden.
5510      * @param {Number} colIndex The column index
5511      * @param {Boolean} hidden True if the column is hidden
5512      */
5513     setHidden : function(colIndex, hidden){
5514         this.config[colIndex].hidden = hidden;
5515         this.totalWidth = null;
5516         this.fireEvent("hiddenchange", this, colIndex, hidden);
5517     },
5518
5519     /**
5520      * Sets the editor for a column.
5521      * @param {Number} col The column index
5522      * @param {Object} editor The editor object
5523      */
5524     setEditor : function(col, editor){
5525         this.config[col].editor = editor;
5526     }
5527 });
5528
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530         if(typeof value == "string" && value.length < 1){
5531             return "&#160;";
5532         }
5533         return value;
5534 };
5535
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5538 /*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 /**
5550  * @class Roo.LoadMask
5551  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5552  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5554  * element's UpdateManager load indicator and will be destroyed after the initial load.
5555  * @constructor
5556  * Create a new LoadMask
5557  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558  * @param {Object} config The config object
5559  */
5560 Roo.LoadMask = function(el, config){
5561     this.el = Roo.get(el);
5562     Roo.apply(this, config);
5563     if(this.store){
5564         this.store.on('beforeload', this.onBeforeLoad, this);
5565         this.store.on('load', this.onLoad, this);
5566         this.store.on('loadexception', this.onLoadException, this);
5567         this.removeMask = false;
5568     }else{
5569         var um = this.el.getUpdateManager();
5570         um.showLoadIndicator = false; // disable the default indicator
5571         um.on('beforeupdate', this.onBeforeLoad, this);
5572         um.on('update', this.onLoad, this);
5573         um.on('failure', this.onLoad, this);
5574         this.removeMask = true;
5575     }
5576 };
5577
5578 Roo.LoadMask.prototype = {
5579     /**
5580      * @cfg {Boolean} removeMask
5581      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5583      */
5584     /**
5585      * @cfg {String} msg
5586      * The text to display in a centered loading message box (defaults to 'Loading...')
5587      */
5588     msg : 'Loading...',
5589     /**
5590      * @cfg {String} msgCls
5591      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5592      */
5593     msgCls : 'x-mask-loading',
5594
5595     /**
5596      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5597      * @type Boolean
5598      */
5599     disabled: false,
5600
5601     /**
5602      * Disables the mask to prevent it from being displayed
5603      */
5604     disable : function(){
5605        this.disabled = true;
5606     },
5607
5608     /**
5609      * Enables the mask so that it can be displayed
5610      */
5611     enable : function(){
5612         this.disabled = false;
5613     },
5614     
5615     onLoadException : function()
5616     {
5617         Roo.log(arguments);
5618         
5619         if (typeof(arguments[3]) != 'undefined') {
5620             Roo.MessageBox.alert("Error loading",arguments[3]);
5621         } 
5622         /*
5623         try {
5624             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5626             }   
5627         } catch(e) {
5628             
5629         }
5630         */
5631     
5632         
5633         
5634         this.el.unmask(this.removeMask);
5635     },
5636     // private
5637     onLoad : function()
5638     {
5639         this.el.unmask(this.removeMask);
5640     },
5641
5642     // private
5643     onBeforeLoad : function(){
5644         if(!this.disabled){
5645             this.el.mask(this.msg, this.msgCls);
5646         }
5647     },
5648
5649     // private
5650     destroy : function(){
5651         if(this.store){
5652             this.store.un('beforeload', this.onBeforeLoad, this);
5653             this.store.un('load', this.onLoad, this);
5654             this.store.un('loadexception', this.onLoadException, this);
5655         }else{
5656             var um = this.el.getUpdateManager();
5657             um.un('beforeupdate', this.onBeforeLoad, this);
5658             um.un('update', this.onLoad, this);
5659             um.un('failure', this.onLoad, this);
5660         }
5661     }
5662 };/*
5663  * - LGPL
5664  *
5665  * table
5666  * 
5667  */
5668
5669 /**
5670  * @class Roo.bootstrap.Table
5671  * @extends Roo.bootstrap.Component
5672  * Bootstrap Table class
5673  * @cfg {String} cls table class
5674  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675  * @cfg {String} bgcolor Specifies the background color for a table
5676  * @cfg {Number} border Specifies whether the table cells should have borders or not
5677  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678  * @cfg {Number} cellspacing Specifies the space between cells
5679  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681  * @cfg {String} sortable Specifies that the table should be sortable
5682  * @cfg {String} summary Specifies a summary of the content of a table
5683  * @cfg {Number} width Specifies the width of a table
5684  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5685  * 
5686  * @cfg {boolean} striped Should the rows be alternative striped
5687  * @cfg {boolean} bordered Add borders to the table
5688  * @cfg {boolean} hover Add hover highlighting
5689  * @cfg {boolean} condensed Format condensed
5690  * @cfg {boolean} responsive Format condensed
5691  * @cfg {Boolean} loadMask (true|false) default false
5692  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694  * @cfg {Boolean} rowSelection (true|false) default false
5695  * @cfg {Boolean} cellSelection (true|false) default false
5696  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5698  
5699  * 
5700  * @constructor
5701  * Create a new Table
5702  * @param {Object} config The config object
5703  */
5704
5705 Roo.bootstrap.Table = function(config){
5706     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5707     
5708   
5709     
5710     // BC...
5711     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5715     
5716     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5717     if (this.sm) {
5718         this.sm.grid = this;
5719         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5720         this.sm = this.selModel;
5721         this.sm.xmodule = this.xmodule || false;
5722     }
5723     
5724     if (this.cm && typeof(this.cm.config) == 'undefined') {
5725         this.colModel = new Roo.grid.ColumnModel(this.cm);
5726         this.cm = this.colModel;
5727         this.cm.xmodule = this.xmodule || false;
5728     }
5729     if (this.store) {
5730         this.store= Roo.factory(this.store, Roo.data);
5731         this.ds = this.store;
5732         this.ds.xmodule = this.xmodule || false;
5733          
5734     }
5735     if (this.footer && this.store) {
5736         this.footer.dataSource = this.ds;
5737         this.footer = Roo.factory(this.footer);
5738     }
5739     
5740     /** @private */
5741     this.addEvents({
5742         /**
5743          * @event cellclick
5744          * Fires when a cell is clicked
5745          * @param {Roo.bootstrap.Table} this
5746          * @param {Roo.Element} el
5747          * @param {Number} rowIndex
5748          * @param {Number} columnIndex
5749          * @param {Roo.EventObject} e
5750          */
5751         "cellclick" : true,
5752         /**
5753          * @event celldblclick
5754          * Fires when a cell is double clicked
5755          * @param {Roo.bootstrap.Table} this
5756          * @param {Roo.Element} el
5757          * @param {Number} rowIndex
5758          * @param {Number} columnIndex
5759          * @param {Roo.EventObject} e
5760          */
5761         "celldblclick" : true,
5762         /**
5763          * @event rowclick
5764          * Fires when a row is clicked
5765          * @param {Roo.bootstrap.Table} this
5766          * @param {Roo.Element} el
5767          * @param {Number} rowIndex
5768          * @param {Roo.EventObject} e
5769          */
5770         "rowclick" : true,
5771         /**
5772          * @event rowdblclick
5773          * Fires when a row is double clicked
5774          * @param {Roo.bootstrap.Table} this
5775          * @param {Roo.Element} el
5776          * @param {Number} rowIndex
5777          * @param {Roo.EventObject} e
5778          */
5779         "rowdblclick" : true,
5780         /**
5781          * @event mouseover
5782          * Fires when a mouseover occur
5783          * @param {Roo.bootstrap.Table} this
5784          * @param {Roo.Element} el
5785          * @param {Number} rowIndex
5786          * @param {Number} columnIndex
5787          * @param {Roo.EventObject} e
5788          */
5789         "mouseover" : true,
5790         /**
5791          * @event mouseout
5792          * Fires when a mouseout occur
5793          * @param {Roo.bootstrap.Table} this
5794          * @param {Roo.Element} el
5795          * @param {Number} rowIndex
5796          * @param {Number} columnIndex
5797          * @param {Roo.EventObject} e
5798          */
5799         "mouseout" : true,
5800         /**
5801          * @event rowclass
5802          * Fires when a row is rendered, so you can change add a style to it.
5803          * @param {Roo.bootstrap.Table} this
5804          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5805          */
5806         'rowclass' : true,
5807           /**
5808          * @event rowsrendered
5809          * Fires when all the  rows have been rendered
5810          * @param {Roo.bootstrap.Table} this
5811          */
5812         'rowsrendered' : true,
5813         /**
5814          * @event contextmenu
5815          * The raw contextmenu event for the entire grid.
5816          * @param {Roo.EventObject} e
5817          */
5818         "contextmenu" : true,
5819         /**
5820          * @event rowcontextmenu
5821          * Fires when a row is right clicked
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Number} rowIndex
5824          * @param {Roo.EventObject} e
5825          */
5826         "rowcontextmenu" : true,
5827         /**
5828          * @event cellcontextmenu
5829          * Fires when a cell is right clicked
5830          * @param {Roo.bootstrap.Table} this
5831          * @param {Number} rowIndex
5832          * @param {Number} cellIndex
5833          * @param {Roo.EventObject} e
5834          */
5835          "cellcontextmenu" : true,
5836          /**
5837          * @event headercontextmenu
5838          * Fires when a header is right clicked
5839          * @param {Roo.bootstrap.Table} this
5840          * @param {Number} columnIndex
5841          * @param {Roo.EventObject} e
5842          */
5843         "headercontextmenu" : true
5844     });
5845 };
5846
5847 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5848     
5849     cls: false,
5850     align: false,
5851     bgcolor: false,
5852     border: false,
5853     cellpadding: false,
5854     cellspacing: false,
5855     frame: false,
5856     rules: false,
5857     sortable: false,
5858     summary: false,
5859     width: false,
5860     striped : false,
5861     scrollBody : false,
5862     bordered: false,
5863     hover:  false,
5864     condensed : false,
5865     responsive : false,
5866     sm : false,
5867     cm : false,
5868     store : false,
5869     loadMask : false,
5870     footerShow : true,
5871     headerShow : true,
5872   
5873     rowSelection : false,
5874     cellSelection : false,
5875     layout : false,
5876     
5877     // Roo.Element - the tbody
5878     mainBody: false,
5879     // Roo.Element - thead element
5880     mainHead: false,
5881     
5882     container: false, // used by gridpanel...
5883     
5884     getAutoCreate : function()
5885     {
5886         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5887         
5888         cfg = {
5889             tag: 'table',
5890             cls : 'table',
5891             cn : []
5892         };
5893         if (this.scrollBody) {
5894             cfg.cls += ' table-body-fixed';
5895         }    
5896         if (this.striped) {
5897             cfg.cls += ' table-striped';
5898         }
5899         
5900         if (this.hover) {
5901             cfg.cls += ' table-hover';
5902         }
5903         if (this.bordered) {
5904             cfg.cls += ' table-bordered';
5905         }
5906         if (this.condensed) {
5907             cfg.cls += ' table-condensed';
5908         }
5909         if (this.responsive) {
5910             cfg.cls += ' table-responsive';
5911         }
5912         
5913         if (this.cls) {
5914             cfg.cls+=  ' ' +this.cls;
5915         }
5916         
5917         // this lot should be simplifed...
5918         
5919         if (this.align) {
5920             cfg.align=this.align;
5921         }
5922         if (this.bgcolor) {
5923             cfg.bgcolor=this.bgcolor;
5924         }
5925         if (this.border) {
5926             cfg.border=this.border;
5927         }
5928         if (this.cellpadding) {
5929             cfg.cellpadding=this.cellpadding;
5930         }
5931         if (this.cellspacing) {
5932             cfg.cellspacing=this.cellspacing;
5933         }
5934         if (this.frame) {
5935             cfg.frame=this.frame;
5936         }
5937         if (this.rules) {
5938             cfg.rules=this.rules;
5939         }
5940         if (this.sortable) {
5941             cfg.sortable=this.sortable;
5942         }
5943         if (this.summary) {
5944             cfg.summary=this.summary;
5945         }
5946         if (this.width) {
5947             cfg.width=this.width;
5948         }
5949         if (this.layout) {
5950             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5951         }
5952         
5953         if(this.store || this.cm){
5954             if(this.headerShow){
5955                 cfg.cn.push(this.renderHeader());
5956             }
5957             
5958             cfg.cn.push(this.renderBody());
5959             
5960             if(this.footerShow){
5961                 cfg.cn.push(this.renderFooter());
5962             }
5963             // where does this come from?
5964             //cfg.cls+=  ' TableGrid';
5965         }
5966         
5967         return { cn : [ cfg ] };
5968     },
5969     
5970     initEvents : function()
5971     {   
5972         if(!this.store || !this.cm){
5973             return;
5974         }
5975         if (this.selModel) {
5976             this.selModel.initEvents();
5977         }
5978         
5979         
5980         //Roo.log('initEvents with ds!!!!');
5981         
5982         this.mainBody = this.el.select('tbody', true).first();
5983         this.mainHead = this.el.select('thead', true).first();
5984         
5985         
5986         
5987         
5988         var _this = this;
5989         
5990         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5991             e.on('click', _this.sort, _this);
5992         });
5993         
5994         this.el.on("click", this.onClick, this);
5995         this.el.on("dblclick", this.onDblClick, this);
5996         
5997         // why is this done????? = it breaks dialogs??
5998         //this.parent().el.setStyle('position', 'relative');
5999         
6000         
6001         if (this.footer) {
6002             this.footer.parentId = this.id;
6003             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6004         }
6005         
6006         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6007         
6008         this.store.on('load', this.onLoad, this);
6009         this.store.on('beforeload', this.onBeforeLoad, this);
6010         this.store.on('update', this.onUpdate, this);
6011         this.store.on('add', this.onAdd, this);
6012         this.store.on("clear", this.clear, this);
6013         
6014         this.el.on("contextmenu", this.onContextMenu, this);
6015         
6016         this.mainBody.on('scroll', this.onBodyScroll, this);
6017         
6018         
6019     },
6020     
6021     onContextMenu : function(e, t)
6022     {
6023         this.processEvent("contextmenu", e);
6024     },
6025     
6026     processEvent : function(name, e)
6027     {
6028         if (name != 'touchstart' ) {
6029             this.fireEvent(name, e);    
6030         }
6031         
6032         var t = e.getTarget();
6033         
6034         var cell = Roo.get(t);
6035         
6036         if(!cell){
6037             return;
6038         }
6039         
6040         if(cell.findParent('tfoot', false, true)){
6041             return;
6042         }
6043         
6044         if(cell.findParent('thead', false, true)){
6045             
6046             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6047                 cell = Roo.get(t).findParent('th', false, true);
6048                 if (!cell) {
6049                     Roo.log("failed to find th in thead?");
6050                     Roo.log(e.getTarget());
6051                     return;
6052                 }
6053             }
6054             
6055             var cellIndex = cell.dom.cellIndex;
6056             
6057             var ename = name == 'touchstart' ? 'click' : name;
6058             this.fireEvent("header" + ename, this, cellIndex, e);
6059             
6060             return;
6061         }
6062         
6063         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6064             cell = Roo.get(t).findParent('td', false, true);
6065             if (!cell) {
6066                 Roo.log("failed to find th in tbody?");
6067                 Roo.log(e.getTarget());
6068                 return;
6069             }
6070         }
6071         
6072         var row = cell.findParent('tr', false, true);
6073         var cellIndex = cell.dom.cellIndex;
6074         var rowIndex = row.dom.rowIndex - 1;
6075         
6076         if(row !== false){
6077             
6078             this.fireEvent("row" + name, this, rowIndex, e);
6079             
6080             if(cell !== false){
6081             
6082                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6083             }
6084         }
6085         
6086     },
6087     
6088     onMouseover : function(e, el)
6089     {
6090         var cell = Roo.get(el);
6091         
6092         if(!cell){
6093             return;
6094         }
6095         
6096         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6097             cell = cell.findParent('td', false, true);
6098         }
6099         
6100         var row = cell.findParent('tr', false, true);
6101         var cellIndex = cell.dom.cellIndex;
6102         var rowIndex = row.dom.rowIndex - 1; // start from 0
6103         
6104         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6105         
6106     },
6107     
6108     onMouseout : function(e, el)
6109     {
6110         var cell = Roo.get(el);
6111         
6112         if(!cell){
6113             return;
6114         }
6115         
6116         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6117             cell = cell.findParent('td', false, true);
6118         }
6119         
6120         var row = cell.findParent('tr', false, true);
6121         var cellIndex = cell.dom.cellIndex;
6122         var rowIndex = row.dom.rowIndex - 1; // start from 0
6123         
6124         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6125         
6126     },
6127     
6128     onClick : function(e, el)
6129     {
6130         var cell = Roo.get(el);
6131         
6132         if(!cell || (!this.cellSelection && !this.rowSelection)){
6133             return;
6134         }
6135         
6136         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6137             cell = cell.findParent('td', false, true);
6138         }
6139         
6140         if(!cell || typeof(cell) == 'undefined'){
6141             return;
6142         }
6143         
6144         var row = cell.findParent('tr', false, true);
6145         
6146         if(!row || typeof(row) == 'undefined'){
6147             return;
6148         }
6149         
6150         var cellIndex = cell.dom.cellIndex;
6151         var rowIndex = this.getRowIndex(row);
6152         
6153         // why??? - should these not be based on SelectionModel?
6154         if(this.cellSelection){
6155             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6156         }
6157         
6158         if(this.rowSelection){
6159             this.fireEvent('rowclick', this, row, rowIndex, e);
6160         }
6161         
6162         
6163     },
6164     
6165     onDblClick : function(e,el)
6166     {
6167         var cell = Roo.get(el);
6168         
6169         if(!cell || (!this.CellSelection && !this.RowSelection)){
6170             return;
6171         }
6172         
6173         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6174             cell = cell.findParent('td', false, true);
6175         }
6176         
6177         if(!cell || typeof(cell) == 'undefined'){
6178             return;
6179         }
6180         
6181         var row = cell.findParent('tr', false, true);
6182         
6183         if(!row || typeof(row) == 'undefined'){
6184             return;
6185         }
6186         
6187         var cellIndex = cell.dom.cellIndex;
6188         var rowIndex = this.getRowIndex(row);
6189         
6190         if(this.CellSelection){
6191             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6192         }
6193         
6194         if(this.RowSelection){
6195             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6196         }
6197     },
6198     
6199     sort : function(e,el)
6200     {
6201         var col = Roo.get(el);
6202         
6203         if(!col.hasClass('sortable')){
6204             return;
6205         }
6206         
6207         var sort = col.attr('sort');
6208         var dir = 'ASC';
6209         
6210         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6211             dir = 'DESC';
6212         }
6213         
6214         this.store.sortInfo = {field : sort, direction : dir};
6215         
6216         if (this.footer) {
6217             Roo.log("calling footer first");
6218             this.footer.onClick('first');
6219         } else {
6220         
6221             this.store.load({ params : { start : 0 } });
6222         }
6223     },
6224     
6225     renderHeader : function()
6226     {
6227         var header = {
6228             tag: 'thead',
6229             cn : []
6230         };
6231         
6232         var cm = this.cm;
6233         this.totalWidth = 0;
6234         
6235         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6236             
6237             var config = cm.config[i];
6238             
6239             var c = {
6240                 tag: 'th',
6241                 style : '',
6242                 html: cm.getColumnHeader(i)
6243             };
6244             
6245             var hh = '';
6246             
6247             if(typeof(config.sortable) != 'undefined' && config.sortable){
6248                 c.cls = 'sortable';
6249                 c.html = '<i class="glyphicon"></i>' + c.html;
6250             }
6251             
6252             if(typeof(config.lgHeader) != 'undefined'){
6253                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6254             }
6255             
6256             if(typeof(config.mdHeader) != 'undefined'){
6257                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6258             }
6259             
6260             if(typeof(config.smHeader) != 'undefined'){
6261                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6262             }
6263             
6264             if(typeof(config.xsHeader) != 'undefined'){
6265                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6266             }
6267             
6268             if(hh.length){
6269                 c.html = hh;
6270             }
6271             
6272             if(typeof(config.tooltip) != 'undefined'){
6273                 c.tooltip = config.tooltip;
6274             }
6275             
6276             if(typeof(config.colspan) != 'undefined'){
6277                 c.colspan = config.colspan;
6278             }
6279             
6280             if(typeof(config.hidden) != 'undefined' && config.hidden){
6281                 c.style += ' display:none;';
6282             }
6283             
6284             if(typeof(config.dataIndex) != 'undefined'){
6285                 c.sort = config.dataIndex;
6286             }
6287             
6288            
6289             
6290             if(typeof(config.align) != 'undefined' && config.align.length){
6291                 c.style += ' text-align:' + config.align + ';';
6292             }
6293             
6294             if(typeof(config.width) != 'undefined'){
6295                 c.style += ' width:' + config.width + 'px;';
6296                 this.totalWidth += config.width;
6297             } else {
6298                 this.totalWidth += 100; // assume minimum of 100 per column?
6299             }
6300             
6301             if(typeof(config.cls) != 'undefined'){
6302                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6303             }
6304             
6305             ['xs','sm','md','lg'].map(function(size){
6306                 
6307                 if(typeof(config[size]) == 'undefined'){
6308                     return;
6309                 }
6310                 
6311                 if (!config[size]) { // 0 = hidden
6312                     c.cls += ' hidden-' + size;
6313                     return;
6314                 }
6315                 
6316                 c.cls += ' col-' + size + '-' + config[size];
6317
6318             });
6319             
6320             header.cn.push(c)
6321         }
6322         
6323         return header;
6324     },
6325     
6326     renderBody : function()
6327     {
6328         var body = {
6329             tag: 'tbody',
6330             cn : [
6331                 {
6332                     tag: 'tr',
6333                     cn : [
6334                         {
6335                             tag : 'td',
6336                             colspan :  this.cm.getColumnCount()
6337                         }
6338                     ]
6339                 }
6340             ]
6341         };
6342         
6343         return body;
6344     },
6345     
6346     renderFooter : function()
6347     {
6348         var footer = {
6349             tag: 'tfoot',
6350             cn : [
6351                 {
6352                     tag: 'tr',
6353                     cn : [
6354                         {
6355                             tag : 'td',
6356                             colspan :  this.cm.getColumnCount()
6357                         }
6358                     ]
6359                 }
6360             ]
6361         };
6362         
6363         return footer;
6364     },
6365     
6366     
6367     
6368     onLoad : function()
6369     {
6370 //        Roo.log('ds onload');
6371         this.clear();
6372         
6373         var _this = this;
6374         var cm = this.cm;
6375         var ds = this.store;
6376         
6377         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6378             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6379             if (_this.store.sortInfo) {
6380                     
6381                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6382                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6383                 }
6384                 
6385                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6386                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6387                 }
6388             }
6389         });
6390         
6391         var tbody =  this.mainBody;
6392               
6393         if(ds.getCount() > 0){
6394             ds.data.each(function(d,rowIndex){
6395                 var row =  this.renderRow(cm, ds, rowIndex);
6396                 
6397                 tbody.createChild(row);
6398                 
6399                 var _this = this;
6400                 
6401                 if(row.cellObjects.length){
6402                     Roo.each(row.cellObjects, function(r){
6403                         _this.renderCellObject(r);
6404                     })
6405                 }
6406                 
6407             }, this);
6408         }
6409         
6410         Roo.each(this.el.select('tbody td', true).elements, function(e){
6411             e.on('mouseover', _this.onMouseover, _this);
6412         });
6413         
6414         Roo.each(this.el.select('tbody td', true).elements, function(e){
6415             e.on('mouseout', _this.onMouseout, _this);
6416         });
6417         this.fireEvent('rowsrendered', this);
6418         //if(this.loadMask){
6419         //    this.maskEl.hide();
6420         //}
6421         
6422         this.autoSize();
6423     },
6424     
6425     
6426     onUpdate : function(ds,record)
6427     {
6428         this.refreshRow(record);
6429     },
6430     
6431     onRemove : function(ds, record, index, isUpdate){
6432         if(isUpdate !== true){
6433             this.fireEvent("beforerowremoved", this, index, record);
6434         }
6435         var bt = this.mainBody.dom;
6436         
6437         var rows = this.el.select('tbody > tr', true).elements;
6438         
6439         if(typeof(rows[index]) != 'undefined'){
6440             bt.removeChild(rows[index].dom);
6441         }
6442         
6443 //        if(bt.rows[index]){
6444 //            bt.removeChild(bt.rows[index]);
6445 //        }
6446         
6447         if(isUpdate !== true){
6448             //this.stripeRows(index);
6449             //this.syncRowHeights(index, index);
6450             //this.layout();
6451             this.fireEvent("rowremoved", this, index, record);
6452         }
6453     },
6454     
6455     onAdd : function(ds, records, rowIndex)
6456     {
6457         //Roo.log('on Add called');
6458         // - note this does not handle multiple adding very well..
6459         var bt = this.mainBody.dom;
6460         for (var i =0 ; i < records.length;i++) {
6461             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6462             //Roo.log(records[i]);
6463             //Roo.log(this.store.getAt(rowIndex+i));
6464             this.insertRow(this.store, rowIndex + i, false);
6465             return;
6466         }
6467         
6468     },
6469     
6470     
6471     refreshRow : function(record){
6472         var ds = this.store, index;
6473         if(typeof record == 'number'){
6474             index = record;
6475             record = ds.getAt(index);
6476         }else{
6477             index = ds.indexOf(record);
6478         }
6479         this.insertRow(ds, index, true);
6480         this.onRemove(ds, record, index+1, true);
6481         //this.syncRowHeights(index, index);
6482         //this.layout();
6483         this.fireEvent("rowupdated", this, index, record);
6484     },
6485     
6486     insertRow : function(dm, rowIndex, isUpdate){
6487         
6488         if(!isUpdate){
6489             this.fireEvent("beforerowsinserted", this, rowIndex);
6490         }
6491             //var s = this.getScrollState();
6492         var row = this.renderRow(this.cm, this.store, rowIndex);
6493         // insert before rowIndex..
6494         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6495         
6496         var _this = this;
6497                 
6498         if(row.cellObjects.length){
6499             Roo.each(row.cellObjects, function(r){
6500                 _this.renderCellObject(r);
6501             })
6502         }
6503             
6504         if(!isUpdate){
6505             this.fireEvent("rowsinserted", this, rowIndex);
6506             //this.syncRowHeights(firstRow, lastRow);
6507             //this.stripeRows(firstRow);
6508             //this.layout();
6509         }
6510         
6511     },
6512     
6513     
6514     getRowDom : function(rowIndex)
6515     {
6516         var rows = this.el.select('tbody > tr', true).elements;
6517         
6518         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6519         
6520     },
6521     // returns the object tree for a tr..
6522   
6523     
6524     renderRow : function(cm, ds, rowIndex) 
6525     {
6526         
6527         var d = ds.getAt(rowIndex);
6528         
6529         var row = {
6530             tag : 'tr',
6531             cn : []
6532         };
6533             
6534         var cellObjects = [];
6535         
6536         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6537             var config = cm.config[i];
6538             
6539             var renderer = cm.getRenderer(i);
6540             var value = '';
6541             var id = false;
6542             
6543             if(typeof(renderer) !== 'undefined'){
6544                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6545             }
6546             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6547             // and are rendered into the cells after the row is rendered - using the id for the element.
6548             
6549             if(typeof(value) === 'object'){
6550                 id = Roo.id();
6551                 cellObjects.push({
6552                     container : id,
6553                     cfg : value 
6554                 })
6555             }
6556             
6557             var rowcfg = {
6558                 record: d,
6559                 rowIndex : rowIndex,
6560                 colIndex : i,
6561                 rowClass : ''
6562             };
6563
6564             this.fireEvent('rowclass', this, rowcfg);
6565             
6566             var td = {
6567                 tag: 'td',
6568                 cls : rowcfg.rowClass,
6569                 style: '',
6570                 html: (typeof(value) === 'object') ? '' : value
6571             };
6572             
6573             if (id) {
6574                 td.id = id;
6575             }
6576             
6577             if(typeof(config.colspan) != 'undefined'){
6578                 td.colspan = config.colspan;
6579             }
6580             
6581             if(typeof(config.hidden) != 'undefined' && config.hidden){
6582                 td.style += ' display:none;';
6583             }
6584             
6585             if(typeof(config.align) != 'undefined' && config.align.length){
6586                 td.style += ' text-align:' + config.align + ';';
6587             }
6588             
6589             if(typeof(config.width) != 'undefined'){
6590                 td.style += ' width:' +  config.width + 'px;';
6591             }
6592             
6593             if(typeof(config.cursor) != 'undefined'){
6594                 td.style += ' cursor:' +  config.cursor + ';';
6595             }
6596             
6597             if(typeof(config.cls) != 'undefined'){
6598                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6599             }
6600             
6601             ['xs','sm','md','lg'].map(function(size){
6602                 
6603                 if(typeof(config[size]) == 'undefined'){
6604                     return;
6605                 }
6606                 
6607                 if (!config[size]) { // 0 = hidden
6608                     td.cls += ' hidden-' + size;
6609                     return;
6610                 }
6611                 
6612                 td.cls += ' col-' + size + '-' + config[size];
6613
6614             });
6615              
6616             row.cn.push(td);
6617            
6618         }
6619         
6620         row.cellObjects = cellObjects;
6621         
6622         return row;
6623           
6624     },
6625     
6626     
6627     
6628     onBeforeLoad : function()
6629     {
6630         //Roo.log('ds onBeforeLoad');
6631         
6632         //this.clear();
6633         
6634         //if(this.loadMask){
6635         //    this.maskEl.show();
6636         //}
6637     },
6638      /**
6639      * Remove all rows
6640      */
6641     clear : function()
6642     {
6643         this.el.select('tbody', true).first().dom.innerHTML = '';
6644     },
6645     /**
6646      * Show or hide a row.
6647      * @param {Number} rowIndex to show or hide
6648      * @param {Boolean} state hide
6649      */
6650     setRowVisibility : function(rowIndex, state)
6651     {
6652         var bt = this.mainBody.dom;
6653         
6654         var rows = this.el.select('tbody > tr', true).elements;
6655         
6656         if(typeof(rows[rowIndex]) == 'undefined'){
6657             return;
6658         }
6659         rows[rowIndex].dom.style.display = state ? '' : 'none';
6660     },
6661     
6662     
6663     getSelectionModel : function(){
6664         if(!this.selModel){
6665             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6666         }
6667         return this.selModel;
6668     },
6669     /*
6670      * Render the Roo.bootstrap object from renderder
6671      */
6672     renderCellObject : function(r)
6673     {
6674         var _this = this;
6675         
6676         var t = r.cfg.render(r.container);
6677         
6678         if(r.cfg.cn){
6679             Roo.each(r.cfg.cn, function(c){
6680                 var child = {
6681                     container: t.getChildContainer(),
6682                     cfg: c
6683                 };
6684                 _this.renderCellObject(child);
6685             })
6686         }
6687     },
6688     
6689     getRowIndex : function(row)
6690     {
6691         var rowIndex = -1;
6692         
6693         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6694             if(el != row){
6695                 return;
6696             }
6697             
6698             rowIndex = index;
6699         });
6700         
6701         return rowIndex;
6702     },
6703      /**
6704      * Returns the grid's underlying element = used by panel.Grid
6705      * @return {Element} The element
6706      */
6707     getGridEl : function(){
6708         return this.el;
6709     },
6710      /**
6711      * Forces a resize - used by panel.Grid
6712      * @return {Element} The element
6713      */
6714     autoSize : function()
6715     {
6716         //var ctr = Roo.get(this.container.dom.parentElement);
6717         var ctr = Roo.get(this.el.dom);
6718         
6719         var thd = this.getGridEl().select('thead',true).first();
6720         var tbd = this.getGridEl().select('tbody', true).first();
6721         
6722         
6723         var cw = ctr.getWidth();
6724         
6725         if (tbd) {
6726             
6727             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6728             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6729             cw -= barsize;
6730         }
6731         cw = Math.max(cw, this.totalWidth);
6732         this.getGridEl().select('tr',true).setWidth(cw);
6733         // resize 'expandable coloumn?
6734         
6735         return; // we doe not have a view in this design..
6736         
6737     },
6738     onBodyScroll: function()
6739     {
6740         
6741         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6742         this.mainHead.setStyle({
6743                     'position' : 'relative',
6744                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6745         });
6746         
6747         
6748     }
6749 });
6750
6751  
6752
6753  /*
6754  * - LGPL
6755  *
6756  * table cell
6757  * 
6758  */
6759
6760 /**
6761  * @class Roo.bootstrap.TableCell
6762  * @extends Roo.bootstrap.Component
6763  * Bootstrap TableCell class
6764  * @cfg {String} html cell contain text
6765  * @cfg {String} cls cell class
6766  * @cfg {String} tag cell tag (td|th) default td
6767  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6768  * @cfg {String} align Aligns the content in a cell
6769  * @cfg {String} axis Categorizes cells
6770  * @cfg {String} bgcolor Specifies the background color of a cell
6771  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6772  * @cfg {Number} colspan Specifies the number of columns a cell should span
6773  * @cfg {String} headers Specifies one or more header cells a cell is related to
6774  * @cfg {Number} height Sets the height of a cell
6775  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6776  * @cfg {Number} rowspan Sets the number of rows a cell should span
6777  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6778  * @cfg {String} valign Vertical aligns the content in a cell
6779  * @cfg {Number} width Specifies the width of a cell
6780  * 
6781  * @constructor
6782  * Create a new TableCell
6783  * @param {Object} config The config object
6784  */
6785
6786 Roo.bootstrap.TableCell = function(config){
6787     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6788 };
6789
6790 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6791     
6792     html: false,
6793     cls: false,
6794     tag: false,
6795     abbr: false,
6796     align: false,
6797     axis: false,
6798     bgcolor: false,
6799     charoff: false,
6800     colspan: false,
6801     headers: false,
6802     height: false,
6803     nowrap: false,
6804     rowspan: false,
6805     scope: false,
6806     valign: false,
6807     width: false,
6808     
6809     
6810     getAutoCreate : function(){
6811         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6812         
6813         cfg = {
6814             tag: 'td'
6815         };
6816         
6817         if(this.tag){
6818             cfg.tag = this.tag;
6819         }
6820         
6821         if (this.html) {
6822             cfg.html=this.html
6823         }
6824         if (this.cls) {
6825             cfg.cls=this.cls
6826         }
6827         if (this.abbr) {
6828             cfg.abbr=this.abbr
6829         }
6830         if (this.align) {
6831             cfg.align=this.align
6832         }
6833         if (this.axis) {
6834             cfg.axis=this.axis
6835         }
6836         if (this.bgcolor) {
6837             cfg.bgcolor=this.bgcolor
6838         }
6839         if (this.charoff) {
6840             cfg.charoff=this.charoff
6841         }
6842         if (this.colspan) {
6843             cfg.colspan=this.colspan
6844         }
6845         if (this.headers) {
6846             cfg.headers=this.headers
6847         }
6848         if (this.height) {
6849             cfg.height=this.height
6850         }
6851         if (this.nowrap) {
6852             cfg.nowrap=this.nowrap
6853         }
6854         if (this.rowspan) {
6855             cfg.rowspan=this.rowspan
6856         }
6857         if (this.scope) {
6858             cfg.scope=this.scope
6859         }
6860         if (this.valign) {
6861             cfg.valign=this.valign
6862         }
6863         if (this.width) {
6864             cfg.width=this.width
6865         }
6866         
6867         
6868         return cfg;
6869     }
6870    
6871 });
6872
6873  
6874
6875  /*
6876  * - LGPL
6877  *
6878  * table row
6879  * 
6880  */
6881
6882 /**
6883  * @class Roo.bootstrap.TableRow
6884  * @extends Roo.bootstrap.Component
6885  * Bootstrap TableRow class
6886  * @cfg {String} cls row class
6887  * @cfg {String} align Aligns the content in a table row
6888  * @cfg {String} bgcolor Specifies a background color for a table row
6889  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6890  * @cfg {String} valign Vertical aligns the content in a table row
6891  * 
6892  * @constructor
6893  * Create a new TableRow
6894  * @param {Object} config The config object
6895  */
6896
6897 Roo.bootstrap.TableRow = function(config){
6898     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6899 };
6900
6901 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6902     
6903     cls: false,
6904     align: false,
6905     bgcolor: false,
6906     charoff: false,
6907     valign: false,
6908     
6909     getAutoCreate : function(){
6910         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6911         
6912         cfg = {
6913             tag: 'tr'
6914         };
6915             
6916         if(this.cls){
6917             cfg.cls = this.cls;
6918         }
6919         if(this.align){
6920             cfg.align = this.align;
6921         }
6922         if(this.bgcolor){
6923             cfg.bgcolor = this.bgcolor;
6924         }
6925         if(this.charoff){
6926             cfg.charoff = this.charoff;
6927         }
6928         if(this.valign){
6929             cfg.valign = this.valign;
6930         }
6931         
6932         return cfg;
6933     }
6934    
6935 });
6936
6937  
6938
6939  /*
6940  * - LGPL
6941  *
6942  * table body
6943  * 
6944  */
6945
6946 /**
6947  * @class Roo.bootstrap.TableBody
6948  * @extends Roo.bootstrap.Component
6949  * Bootstrap TableBody class
6950  * @cfg {String} cls element class
6951  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6952  * @cfg {String} align Aligns the content inside the element
6953  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6954  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6955  * 
6956  * @constructor
6957  * Create a new TableBody
6958  * @param {Object} config The config object
6959  */
6960
6961 Roo.bootstrap.TableBody = function(config){
6962     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6963 };
6964
6965 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6966     
6967     cls: false,
6968     tag: false,
6969     align: false,
6970     charoff: false,
6971     valign: false,
6972     
6973     getAutoCreate : function(){
6974         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6975         
6976         cfg = {
6977             tag: 'tbody'
6978         };
6979             
6980         if (this.cls) {
6981             cfg.cls=this.cls
6982         }
6983         if(this.tag){
6984             cfg.tag = this.tag;
6985         }
6986         
6987         if(this.align){
6988             cfg.align = this.align;
6989         }
6990         if(this.charoff){
6991             cfg.charoff = this.charoff;
6992         }
6993         if(this.valign){
6994             cfg.valign = this.valign;
6995         }
6996         
6997         return cfg;
6998     }
6999     
7000     
7001 //    initEvents : function()
7002 //    {
7003 //        
7004 //        if(!this.store){
7005 //            return;
7006 //        }
7007 //        
7008 //        this.store = Roo.factory(this.store, Roo.data);
7009 //        this.store.on('load', this.onLoad, this);
7010 //        
7011 //        this.store.load();
7012 //        
7013 //    },
7014 //    
7015 //    onLoad: function () 
7016 //    {   
7017 //        this.fireEvent('load', this);
7018 //    }
7019 //    
7020 //   
7021 });
7022
7023  
7024
7025  /*
7026  * Based on:
7027  * Ext JS Library 1.1.1
7028  * Copyright(c) 2006-2007, Ext JS, LLC.
7029  *
7030  * Originally Released Under LGPL - original licence link has changed is not relivant.
7031  *
7032  * Fork - LGPL
7033  * <script type="text/javascript">
7034  */
7035
7036 // as we use this in bootstrap.
7037 Roo.namespace('Roo.form');
7038  /**
7039  * @class Roo.form.Action
7040  * Internal Class used to handle form actions
7041  * @constructor
7042  * @param {Roo.form.BasicForm} el The form element or its id
7043  * @param {Object} config Configuration options
7044  */
7045
7046  
7047  
7048 // define the action interface
7049 Roo.form.Action = function(form, options){
7050     this.form = form;
7051     this.options = options || {};
7052 };
7053 /**
7054  * Client Validation Failed
7055  * @const 
7056  */
7057 Roo.form.Action.CLIENT_INVALID = 'client';
7058 /**
7059  * Server Validation Failed
7060  * @const 
7061  */
7062 Roo.form.Action.SERVER_INVALID = 'server';
7063  /**
7064  * Connect to Server Failed
7065  * @const 
7066  */
7067 Roo.form.Action.CONNECT_FAILURE = 'connect';
7068 /**
7069  * Reading Data from Server Failed
7070  * @const 
7071  */
7072 Roo.form.Action.LOAD_FAILURE = 'load';
7073
7074 Roo.form.Action.prototype = {
7075     type : 'default',
7076     failureType : undefined,
7077     response : undefined,
7078     result : undefined,
7079
7080     // interface method
7081     run : function(options){
7082
7083     },
7084
7085     // interface method
7086     success : function(response){
7087
7088     },
7089
7090     // interface method
7091     handleResponse : function(response){
7092
7093     },
7094
7095     // default connection failure
7096     failure : function(response){
7097         
7098         this.response = response;
7099         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7100         this.form.afterAction(this, false);
7101     },
7102
7103     processResponse : function(response){
7104         this.response = response;
7105         if(!response.responseText){
7106             return true;
7107         }
7108         this.result = this.handleResponse(response);
7109         return this.result;
7110     },
7111
7112     // utility functions used internally
7113     getUrl : function(appendParams){
7114         var url = this.options.url || this.form.url || this.form.el.dom.action;
7115         if(appendParams){
7116             var p = this.getParams();
7117             if(p){
7118                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7119             }
7120         }
7121         return url;
7122     },
7123
7124     getMethod : function(){
7125         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7126     },
7127
7128     getParams : function(){
7129         var bp = this.form.baseParams;
7130         var p = this.options.params;
7131         if(p){
7132             if(typeof p == "object"){
7133                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7134             }else if(typeof p == 'string' && bp){
7135                 p += '&' + Roo.urlEncode(bp);
7136             }
7137         }else if(bp){
7138             p = Roo.urlEncode(bp);
7139         }
7140         return p;
7141     },
7142
7143     createCallback : function(){
7144         return {
7145             success: this.success,
7146             failure: this.failure,
7147             scope: this,
7148             timeout: (this.form.timeout*1000),
7149             upload: this.form.fileUpload ? this.success : undefined
7150         };
7151     }
7152 };
7153
7154 Roo.form.Action.Submit = function(form, options){
7155     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7156 };
7157
7158 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7159     type : 'submit',
7160
7161     haveProgress : false,
7162     uploadComplete : false,
7163     
7164     // uploadProgress indicator.
7165     uploadProgress : function()
7166     {
7167         if (!this.form.progressUrl) {
7168             return;
7169         }
7170         
7171         if (!this.haveProgress) {
7172             Roo.MessageBox.progress("Uploading", "Uploading");
7173         }
7174         if (this.uploadComplete) {
7175            Roo.MessageBox.hide();
7176            return;
7177         }
7178         
7179         this.haveProgress = true;
7180    
7181         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7182         
7183         var c = new Roo.data.Connection();
7184         c.request({
7185             url : this.form.progressUrl,
7186             params: {
7187                 id : uid
7188             },
7189             method: 'GET',
7190             success : function(req){
7191                //console.log(data);
7192                 var rdata = false;
7193                 var edata;
7194                 try  {
7195                    rdata = Roo.decode(req.responseText)
7196                 } catch (e) {
7197                     Roo.log("Invalid data from server..");
7198                     Roo.log(edata);
7199                     return;
7200                 }
7201                 if (!rdata || !rdata.success) {
7202                     Roo.log(rdata);
7203                     Roo.MessageBox.alert(Roo.encode(rdata));
7204                     return;
7205                 }
7206                 var data = rdata.data;
7207                 
7208                 if (this.uploadComplete) {
7209                    Roo.MessageBox.hide();
7210                    return;
7211                 }
7212                    
7213                 if (data){
7214                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7215                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7216                     );
7217                 }
7218                 this.uploadProgress.defer(2000,this);
7219             },
7220        
7221             failure: function(data) {
7222                 Roo.log('progress url failed ');
7223                 Roo.log(data);
7224             },
7225             scope : this
7226         });
7227            
7228     },
7229     
7230     
7231     run : function()
7232     {
7233         // run get Values on the form, so it syncs any secondary forms.
7234         this.form.getValues();
7235         
7236         var o = this.options;
7237         var method = this.getMethod();
7238         var isPost = method == 'POST';
7239         if(o.clientValidation === false || this.form.isValid()){
7240             
7241             if (this.form.progressUrl) {
7242                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7243                     (new Date() * 1) + '' + Math.random());
7244                     
7245             } 
7246             
7247             
7248             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7249                 form:this.form.el.dom,
7250                 url:this.getUrl(!isPost),
7251                 method: method,
7252                 params:isPost ? this.getParams() : null,
7253                 isUpload: this.form.fileUpload
7254             }));
7255             
7256             this.uploadProgress();
7257
7258         }else if (o.clientValidation !== false){ // client validation failed
7259             this.failureType = Roo.form.Action.CLIENT_INVALID;
7260             this.form.afterAction(this, false);
7261         }
7262     },
7263
7264     success : function(response)
7265     {
7266         this.uploadComplete= true;
7267         if (this.haveProgress) {
7268             Roo.MessageBox.hide();
7269         }
7270         
7271         
7272         var result = this.processResponse(response);
7273         if(result === true || result.success){
7274             this.form.afterAction(this, true);
7275             return;
7276         }
7277         if(result.errors){
7278             this.form.markInvalid(result.errors);
7279             this.failureType = Roo.form.Action.SERVER_INVALID;
7280         }
7281         this.form.afterAction(this, false);
7282     },
7283     failure : function(response)
7284     {
7285         this.uploadComplete= true;
7286         if (this.haveProgress) {
7287             Roo.MessageBox.hide();
7288         }
7289         
7290         this.response = response;
7291         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7292         this.form.afterAction(this, false);
7293     },
7294     
7295     handleResponse : function(response){
7296         if(this.form.errorReader){
7297             var rs = this.form.errorReader.read(response);
7298             var errors = [];
7299             if(rs.records){
7300                 for(var i = 0, len = rs.records.length; i < len; i++) {
7301                     var r = rs.records[i];
7302                     errors[i] = r.data;
7303                 }
7304             }
7305             if(errors.length < 1){
7306                 errors = null;
7307             }
7308             return {
7309                 success : rs.success,
7310                 errors : errors
7311             };
7312         }
7313         var ret = false;
7314         try {
7315             ret = Roo.decode(response.responseText);
7316         } catch (e) {
7317             ret = {
7318                 success: false,
7319                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7320                 errors : []
7321             };
7322         }
7323         return ret;
7324         
7325     }
7326 });
7327
7328
7329 Roo.form.Action.Load = function(form, options){
7330     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7331     this.reader = this.form.reader;
7332 };
7333
7334 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7335     type : 'load',
7336
7337     run : function(){
7338         
7339         Roo.Ajax.request(Roo.apply(
7340                 this.createCallback(), {
7341                     method:this.getMethod(),
7342                     url:this.getUrl(false),
7343                     params:this.getParams()
7344         }));
7345     },
7346
7347     success : function(response){
7348         
7349         var result = this.processResponse(response);
7350         if(result === true || !result.success || !result.data){
7351             this.failureType = Roo.form.Action.LOAD_FAILURE;
7352             this.form.afterAction(this, false);
7353             return;
7354         }
7355         this.form.clearInvalid();
7356         this.form.setValues(result.data);
7357         this.form.afterAction(this, true);
7358     },
7359
7360     handleResponse : function(response){
7361         if(this.form.reader){
7362             var rs = this.form.reader.read(response);
7363             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7364             return {
7365                 success : rs.success,
7366                 data : data
7367             };
7368         }
7369         return Roo.decode(response.responseText);
7370     }
7371 });
7372
7373 Roo.form.Action.ACTION_TYPES = {
7374     'load' : Roo.form.Action.Load,
7375     'submit' : Roo.form.Action.Submit
7376 };/*
7377  * - LGPL
7378  *
7379  * form
7380  * 
7381  */
7382
7383 /**
7384  * @class Roo.bootstrap.Form
7385  * @extends Roo.bootstrap.Component
7386  * Bootstrap Form class
7387  * @cfg {String} method  GET | POST (default POST)
7388  * @cfg {String} labelAlign top | left (default top)
7389  * @cfg {String} align left  | right - for navbars
7390  * @cfg {Boolean} loadMask load mask when submit (default true)
7391
7392  * 
7393  * @constructor
7394  * Create a new Form
7395  * @param {Object} config The config object
7396  */
7397
7398
7399 Roo.bootstrap.Form = function(config){
7400     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7401     this.addEvents({
7402         /**
7403          * @event clientvalidation
7404          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7405          * @param {Form} this
7406          * @param {Boolean} valid true if the form has passed client-side validation
7407          */
7408         clientvalidation: true,
7409         /**
7410          * @event beforeaction
7411          * Fires before any action is performed. Return false to cancel the action.
7412          * @param {Form} this
7413          * @param {Action} action The action to be performed
7414          */
7415         beforeaction: true,
7416         /**
7417          * @event actionfailed
7418          * Fires when an action fails.
7419          * @param {Form} this
7420          * @param {Action} action The action that failed
7421          */
7422         actionfailed : true,
7423         /**
7424          * @event actioncomplete
7425          * Fires when an action is completed.
7426          * @param {Form} this
7427          * @param {Action} action The action that completed
7428          */
7429         actioncomplete : true
7430     });
7431     
7432 };
7433
7434 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7435       
7436      /**
7437      * @cfg {String} method
7438      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7439      */
7440     method : 'POST',
7441     /**
7442      * @cfg {String} url
7443      * The URL to use for form actions if one isn't supplied in the action options.
7444      */
7445     /**
7446      * @cfg {Boolean} fileUpload
7447      * Set to true if this form is a file upload.
7448      */
7449      
7450     /**
7451      * @cfg {Object} baseParams
7452      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7453      */
7454       
7455     /**
7456      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7457      */
7458     timeout: 30,
7459     /**
7460      * @cfg {Sting} align (left|right) for navbar forms
7461      */
7462     align : 'left',
7463
7464     // private
7465     activeAction : null,
7466  
7467     /**
7468      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7469      * element by passing it or its id or mask the form itself by passing in true.
7470      * @type Mixed
7471      */
7472     waitMsgTarget : false,
7473     
7474     loadMask : true,
7475     
7476     getAutoCreate : function(){
7477         
7478         var cfg = {
7479             tag: 'form',
7480             method : this.method || 'POST',
7481             id : this.id || Roo.id(),
7482             cls : ''
7483         };
7484         if (this.parent().xtype.match(/^Nav/)) {
7485             cfg.cls = 'navbar-form navbar-' + this.align;
7486             
7487         }
7488         
7489         if (this.labelAlign == 'left' ) {
7490             cfg.cls += ' form-horizontal';
7491         }
7492         
7493         
7494         return cfg;
7495     },
7496     initEvents : function()
7497     {
7498         this.el.on('submit', this.onSubmit, this);
7499         // this was added as random key presses on the form where triggering form submit.
7500         this.el.on('keypress', function(e) {
7501             if (e.getCharCode() != 13) {
7502                 return true;
7503             }
7504             // we might need to allow it for textareas.. and some other items.
7505             // check e.getTarget().
7506             
7507             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7508                 return true;
7509             }
7510         
7511             Roo.log("keypress blocked");
7512             
7513             e.preventDefault();
7514             return false;
7515         });
7516         
7517     },
7518     // private
7519     onSubmit : function(e){
7520         e.stopEvent();
7521     },
7522     
7523      /**
7524      * Returns true if client-side validation on the form is successful.
7525      * @return Boolean
7526      */
7527     isValid : function(){
7528         var items = this.getItems();
7529         var valid = true;
7530         items.each(function(f){
7531            if(!f.validate()){
7532                valid = false;
7533                
7534            }
7535         });
7536         return valid;
7537     },
7538     /**
7539      * Returns true if any fields in this form have changed since their original load.
7540      * @return Boolean
7541      */
7542     isDirty : function(){
7543         var dirty = false;
7544         var items = this.getItems();
7545         items.each(function(f){
7546            if(f.isDirty()){
7547                dirty = true;
7548                return false;
7549            }
7550            return true;
7551         });
7552         return dirty;
7553     },
7554      /**
7555      * Performs a predefined action (submit or load) or custom actions you define on this form.
7556      * @param {String} actionName The name of the action type
7557      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7558      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7559      * accept other config options):
7560      * <pre>
7561 Property          Type             Description
7562 ----------------  ---------------  ----------------------------------------------------------------------------------
7563 url               String           The url for the action (defaults to the form's url)
7564 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7565 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7566 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7567                                    validate the form on the client (defaults to false)
7568      * </pre>
7569      * @return {BasicForm} this
7570      */
7571     doAction : function(action, options){
7572         if(typeof action == 'string'){
7573             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7574         }
7575         if(this.fireEvent('beforeaction', this, action) !== false){
7576             this.beforeAction(action);
7577             action.run.defer(100, action);
7578         }
7579         return this;
7580     },
7581     
7582     // private
7583     beforeAction : function(action){
7584         var o = action.options;
7585         
7586         if(this.loadMask){
7587             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7588         }
7589         // not really supported yet.. ??
7590         
7591         //if(this.waitMsgTarget === true){
7592         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7593         //}else if(this.waitMsgTarget){
7594         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7595         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7596         //}else {
7597         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7598        // }
7599          
7600     },
7601
7602     // private
7603     afterAction : function(action, success){
7604         this.activeAction = null;
7605         var o = action.options;
7606         
7607         //if(this.waitMsgTarget === true){
7608             this.el.unmask();
7609         //}else if(this.waitMsgTarget){
7610         //    this.waitMsgTarget.unmask();
7611         //}else{
7612         //    Roo.MessageBox.updateProgress(1);
7613         //    Roo.MessageBox.hide();
7614        // }
7615         // 
7616         if(success){
7617             if(o.reset){
7618                 this.reset();
7619             }
7620             Roo.callback(o.success, o.scope, [this, action]);
7621             this.fireEvent('actioncomplete', this, action);
7622             
7623         }else{
7624             
7625             // failure condition..
7626             // we have a scenario where updates need confirming.
7627             // eg. if a locking scenario exists..
7628             // we look for { errors : { needs_confirm : true }} in the response.
7629             if (
7630                 (typeof(action.result) != 'undefined')  &&
7631                 (typeof(action.result.errors) != 'undefined')  &&
7632                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7633            ){
7634                 var _t = this;
7635                 Roo.log("not supported yet");
7636                  /*
7637                 
7638                 Roo.MessageBox.confirm(
7639                     "Change requires confirmation",
7640                     action.result.errorMsg,
7641                     function(r) {
7642                         if (r != 'yes') {
7643                             return;
7644                         }
7645                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7646                     }
7647                     
7648                 );
7649                 */
7650                 
7651                 
7652                 return;
7653             }
7654             
7655             Roo.callback(o.failure, o.scope, [this, action]);
7656             // show an error message if no failed handler is set..
7657             if (!this.hasListener('actionfailed')) {
7658                 Roo.log("need to add dialog support");
7659                 /*
7660                 Roo.MessageBox.alert("Error",
7661                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7662                         action.result.errorMsg :
7663                         "Saving Failed, please check your entries or try again"
7664                 );
7665                 */
7666             }
7667             
7668             this.fireEvent('actionfailed', this, action);
7669         }
7670         
7671     },
7672     /**
7673      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7674      * @param {String} id The value to search for
7675      * @return Field
7676      */
7677     findField : function(id){
7678         var items = this.getItems();
7679         var field = items.get(id);
7680         if(!field){
7681              items.each(function(f){
7682                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7683                     field = f;
7684                     return false;
7685                 }
7686                 return true;
7687             });
7688         }
7689         return field || null;
7690     },
7691      /**
7692      * Mark fields in this form invalid in bulk.
7693      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7694      * @return {BasicForm} this
7695      */
7696     markInvalid : function(errors){
7697         if(errors instanceof Array){
7698             for(var i = 0, len = errors.length; i < len; i++){
7699                 var fieldError = errors[i];
7700                 var f = this.findField(fieldError.id);
7701                 if(f){
7702                     f.markInvalid(fieldError.msg);
7703                 }
7704             }
7705         }else{
7706             var field, id;
7707             for(id in errors){
7708                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7709                     field.markInvalid(errors[id]);
7710                 }
7711             }
7712         }
7713         //Roo.each(this.childForms || [], function (f) {
7714         //    f.markInvalid(errors);
7715         //});
7716         
7717         return this;
7718     },
7719
7720     /**
7721      * Set values for fields in this form in bulk.
7722      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7723      * @return {BasicForm} this
7724      */
7725     setValues : function(values){
7726         if(values instanceof Array){ // array of objects
7727             for(var i = 0, len = values.length; i < len; i++){
7728                 var v = values[i];
7729                 var f = this.findField(v.id);
7730                 if(f){
7731                     f.setValue(v.value);
7732                     if(this.trackResetOnLoad){
7733                         f.originalValue = f.getValue();
7734                     }
7735                 }
7736             }
7737         }else{ // object hash
7738             var field, id;
7739             for(id in values){
7740                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7741                     
7742                     if (field.setFromData && 
7743                         field.valueField && 
7744                         field.displayField &&
7745                         // combos' with local stores can 
7746                         // be queried via setValue()
7747                         // to set their value..
7748                         (field.store && !field.store.isLocal)
7749                         ) {
7750                         // it's a combo
7751                         var sd = { };
7752                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7753                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7754                         field.setFromData(sd);
7755                         
7756                     } else {
7757                         field.setValue(values[id]);
7758                     }
7759                     
7760                     
7761                     if(this.trackResetOnLoad){
7762                         field.originalValue = field.getValue();
7763                     }
7764                 }
7765             }
7766         }
7767          
7768         //Roo.each(this.childForms || [], function (f) {
7769         //    f.setValues(values);
7770         //});
7771                 
7772         return this;
7773     },
7774
7775     /**
7776      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7777      * they are returned as an array.
7778      * @param {Boolean} asString
7779      * @return {Object}
7780      */
7781     getValues : function(asString){
7782         //if (this.childForms) {
7783             // copy values from the child forms
7784         //    Roo.each(this.childForms, function (f) {
7785         //        this.setValues(f.getValues());
7786         //    }, this);
7787         //}
7788         
7789         
7790         
7791         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7792         if(asString === true){
7793             return fs;
7794         }
7795         return Roo.urlDecode(fs);
7796     },
7797     
7798     /**
7799      * Returns the fields in this form as an object with key/value pairs. 
7800      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7801      * @return {Object}
7802      */
7803     getFieldValues : function(with_hidden)
7804     {
7805         var items = this.getItems();
7806         var ret = {};
7807         items.each(function(f){
7808             if (!f.getName()) {
7809                 return;
7810             }
7811             var v = f.getValue();
7812             if (f.inputType =='radio') {
7813                 if (typeof(ret[f.getName()]) == 'undefined') {
7814                     ret[f.getName()] = ''; // empty..
7815                 }
7816                 
7817                 if (!f.el.dom.checked) {
7818                     return;
7819                     
7820                 }
7821                 v = f.el.dom.value;
7822                 
7823             }
7824             
7825             // not sure if this supported any more..
7826             if ((typeof(v) == 'object') && f.getRawValue) {
7827                 v = f.getRawValue() ; // dates..
7828             }
7829             // combo boxes where name != hiddenName...
7830             if (f.name != f.getName()) {
7831                 ret[f.name] = f.getRawValue();
7832             }
7833             ret[f.getName()] = v;
7834         });
7835         
7836         return ret;
7837     },
7838
7839     /**
7840      * Clears all invalid messages in this form.
7841      * @return {BasicForm} this
7842      */
7843     clearInvalid : function(){
7844         var items = this.getItems();
7845         
7846         items.each(function(f){
7847            f.clearInvalid();
7848         });
7849         
7850         
7851         
7852         return this;
7853     },
7854
7855     /**
7856      * Resets this form.
7857      * @return {BasicForm} this
7858      */
7859     reset : function(){
7860         var items = this.getItems();
7861         items.each(function(f){
7862             f.reset();
7863         });
7864         
7865         Roo.each(this.childForms || [], function (f) {
7866             f.reset();
7867         });
7868        
7869         
7870         return this;
7871     },
7872     getItems : function()
7873     {
7874         var r=new Roo.util.MixedCollection(false, function(o){
7875             return o.id || (o.id = Roo.id());
7876         });
7877         var iter = function(el) {
7878             if (el.inputEl) {
7879                 r.add(el);
7880             }
7881             if (!el.items) {
7882                 return;
7883             }
7884             Roo.each(el.items,function(e) {
7885                 iter(e);
7886             });
7887             
7888             
7889         };
7890         
7891         iter(this);
7892         return r;
7893         
7894         
7895         
7896         
7897     }
7898     
7899 });
7900
7901  
7902 /*
7903  * Based on:
7904  * Ext JS Library 1.1.1
7905  * Copyright(c) 2006-2007, Ext JS, LLC.
7906  *
7907  * Originally Released Under LGPL - original licence link has changed is not relivant.
7908  *
7909  * Fork - LGPL
7910  * <script type="text/javascript">
7911  */
7912 /**
7913  * @class Roo.form.VTypes
7914  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7915  * @singleton
7916  */
7917 Roo.form.VTypes = function(){
7918     // closure these in so they are only created once.
7919     var alpha = /^[a-zA-Z_]+$/;
7920     var alphanum = /^[a-zA-Z0-9_]+$/;
7921     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7922     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7923
7924     // All these messages and functions are configurable
7925     return {
7926         /**
7927          * The function used to validate email addresses
7928          * @param {String} value The email address
7929          */
7930         'email' : function(v){
7931             return email.test(v);
7932         },
7933         /**
7934          * The error text to display when the email validation function returns false
7935          * @type String
7936          */
7937         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7938         /**
7939          * The keystroke filter mask to be applied on email input
7940          * @type RegExp
7941          */
7942         'emailMask' : /[a-z0-9_\.\-@]/i,
7943
7944         /**
7945          * The function used to validate URLs
7946          * @param {String} value The URL
7947          */
7948         'url' : function(v){
7949             return url.test(v);
7950         },
7951         /**
7952          * The error text to display when the url validation function returns false
7953          * @type String
7954          */
7955         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7956         
7957         /**
7958          * The function used to validate alpha values
7959          * @param {String} value The value
7960          */
7961         'alpha' : function(v){
7962             return alpha.test(v);
7963         },
7964         /**
7965          * The error text to display when the alpha validation function returns false
7966          * @type String
7967          */
7968         'alphaText' : 'This field should only contain letters and _',
7969         /**
7970          * The keystroke filter mask to be applied on alpha input
7971          * @type RegExp
7972          */
7973         'alphaMask' : /[a-z_]/i,
7974
7975         /**
7976          * The function used to validate alphanumeric values
7977          * @param {String} value The value
7978          */
7979         'alphanum' : function(v){
7980             return alphanum.test(v);
7981         },
7982         /**
7983          * The error text to display when the alphanumeric validation function returns false
7984          * @type String
7985          */
7986         'alphanumText' : 'This field should only contain letters, numbers and _',
7987         /**
7988          * The keystroke filter mask to be applied on alphanumeric input
7989          * @type RegExp
7990          */
7991         'alphanumMask' : /[a-z0-9_]/i
7992     };
7993 }();/*
7994  * - LGPL
7995  *
7996  * Input
7997  * 
7998  */
7999
8000 /**
8001  * @class Roo.bootstrap.Input
8002  * @extends Roo.bootstrap.Component
8003  * Bootstrap Input class
8004  * @cfg {Boolean} disabled is it disabled
8005  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8006  * @cfg {String} name name of the input
8007  * @cfg {string} fieldLabel - the label associated
8008  * @cfg {string} placeholder - placeholder to put in text.
8009  * @cfg {string}  before - input group add on before
8010  * @cfg {string} after - input group add on after
8011  * @cfg {string} size - (lg|sm) or leave empty..
8012  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8013  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8014  * @cfg {Number} md colspan out of 12 for computer-sized screens
8015  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8016  * @cfg {string} value default value of the input
8017  * @cfg {Number} labelWidth set the width of label (0-12)
8018  * @cfg {String} labelAlign (top|left)
8019  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8020  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8021  * @cfg {String} indicatorpos (left|right) default left
8022
8023  * @cfg {String} align (left|center|right) Default left
8024  * @cfg {Boolean} forceFeedback (true|false) Default false
8025  * 
8026  * 
8027  * 
8028  * 
8029  * @constructor
8030  * Create a new Input
8031  * @param {Object} config The config object
8032  */
8033
8034 Roo.bootstrap.Input = function(config){
8035     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8036    
8037         this.addEvents({
8038             /**
8039              * @event focus
8040              * Fires when this field receives input focus.
8041              * @param {Roo.form.Field} this
8042              */
8043             focus : true,
8044             /**
8045              * @event blur
8046              * Fires when this field loses input focus.
8047              * @param {Roo.form.Field} this
8048              */
8049             blur : true,
8050             /**
8051              * @event specialkey
8052              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8053              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8054              * @param {Roo.form.Field} this
8055              * @param {Roo.EventObject} e The event object
8056              */
8057             specialkey : true,
8058             /**
8059              * @event change
8060              * Fires just before the field blurs if the field value has changed.
8061              * @param {Roo.form.Field} this
8062              * @param {Mixed} newValue The new value
8063              * @param {Mixed} oldValue The original value
8064              */
8065             change : true,
8066             /**
8067              * @event invalid
8068              * Fires after the field has been marked as invalid.
8069              * @param {Roo.form.Field} this
8070              * @param {String} msg The validation message
8071              */
8072             invalid : true,
8073             /**
8074              * @event valid
8075              * Fires after the field has been validated with no errors.
8076              * @param {Roo.form.Field} this
8077              */
8078             valid : true,
8079              /**
8080              * @event keyup
8081              * Fires after the key up
8082              * @param {Roo.form.Field} this
8083              * @param {Roo.EventObject}  e The event Object
8084              */
8085             keyup : true
8086         });
8087 };
8088
8089 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8090      /**
8091      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8092       automatic validation (defaults to "keyup").
8093      */
8094     validationEvent : "keyup",
8095      /**
8096      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8097      */
8098     validateOnBlur : true,
8099     /**
8100      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8101      */
8102     validationDelay : 250,
8103      /**
8104      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8105      */
8106     focusClass : "x-form-focus",  // not needed???
8107     
8108        
8109     /**
8110      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8111      */
8112     invalidClass : "has-warning",
8113     
8114     /**
8115      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8116      */
8117     validClass : "has-success",
8118     
8119     /**
8120      * @cfg {Boolean} hasFeedback (true|false) default true
8121      */
8122     hasFeedback : true,
8123     
8124     /**
8125      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8126      */
8127     invalidFeedbackClass : "glyphicon-warning-sign",
8128     
8129     /**
8130      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8131      */
8132     validFeedbackClass : "glyphicon-ok",
8133     
8134     /**
8135      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8136      */
8137     selectOnFocus : false,
8138     
8139      /**
8140      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8141      */
8142     maskRe : null,
8143        /**
8144      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8145      */
8146     vtype : null,
8147     
8148       /**
8149      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8150      */
8151     disableKeyFilter : false,
8152     
8153        /**
8154      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8155      */
8156     disabled : false,
8157      /**
8158      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8159      */
8160     allowBlank : true,
8161     /**
8162      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8163      */
8164     blankText : "This field is required",
8165     
8166      /**
8167      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8168      */
8169     minLength : 0,
8170     /**
8171      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8172      */
8173     maxLength : Number.MAX_VALUE,
8174     /**
8175      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8176      */
8177     minLengthText : "The minimum length for this field is {0}",
8178     /**
8179      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8180      */
8181     maxLengthText : "The maximum length for this field is {0}",
8182   
8183     
8184     /**
8185      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8186      * If available, this function will be called only after the basic validators all return true, and will be passed the
8187      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8188      */
8189     validator : null,
8190     /**
8191      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8192      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8193      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8194      */
8195     regex : null,
8196     /**
8197      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8198      */
8199     regexText : "",
8200     
8201     autocomplete: false,
8202     
8203     
8204     fieldLabel : '',
8205     inputType : 'text',
8206     
8207     name : false,
8208     placeholder: false,
8209     before : false,
8210     after : false,
8211     size : false,
8212     hasFocus : false,
8213     preventMark: false,
8214     isFormField : true,
8215     value : '',
8216     labelWidth : 2,
8217     labelAlign : false,
8218     readOnly : false,
8219     align : false,
8220     formatedValue : false,
8221     forceFeedback : false,
8222     
8223     indicatorpos : 'left',
8224     
8225     parentLabelAlign : function()
8226     {
8227         var parent = this;
8228         while (parent.parent()) {
8229             parent = parent.parent();
8230             if (typeof(parent.labelAlign) !='undefined') {
8231                 return parent.labelAlign;
8232             }
8233         }
8234         return 'left';
8235         
8236     },
8237     
8238     getAutoCreate : function()
8239     {
8240         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8241         
8242         var id = Roo.id();
8243         
8244         var cfg = {};
8245         
8246         if(this.inputType != 'hidden'){
8247             cfg.cls = 'form-group' //input-group
8248         }
8249         
8250         var input =  {
8251             tag: 'input',
8252             id : id,
8253             type : this.inputType,
8254             value : this.value,
8255             cls : 'form-control',
8256             placeholder : this.placeholder || '',
8257             autocomplete : this.autocomplete || 'new-password'
8258         };
8259         
8260         if(this.align){
8261             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8262         }
8263         
8264         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8265             input.maxLength = this.maxLength;
8266         }
8267         
8268         if (this.disabled) {
8269             input.disabled=true;
8270         }
8271         
8272         if (this.readOnly) {
8273             input.readonly=true;
8274         }
8275         
8276         if (this.name) {
8277             input.name = this.name;
8278         }
8279         
8280         if (this.size) {
8281             input.cls += ' input-' + this.size;
8282         }
8283         
8284         var settings=this;
8285         ['xs','sm','md','lg'].map(function(size){
8286             if (settings[size]) {
8287                 cfg.cls += ' col-' + size + '-' + settings[size];
8288             }
8289         });
8290         
8291         var inputblock = input;
8292         
8293         var feedback = {
8294             tag: 'span',
8295             cls: 'glyphicon form-control-feedback'
8296         };
8297             
8298         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8299             
8300             inputblock = {
8301                 cls : 'has-feedback',
8302                 cn :  [
8303                     input,
8304                     feedback
8305                 ] 
8306             };  
8307         }
8308         
8309         if (this.before || this.after) {
8310             
8311             inputblock = {
8312                 cls : 'input-group',
8313                 cn :  [] 
8314             };
8315             
8316             if (this.before && typeof(this.before) == 'string') {
8317                 
8318                 inputblock.cn.push({
8319                     tag :'span',
8320                     cls : 'roo-input-before input-group-addon',
8321                     html : this.before
8322                 });
8323             }
8324             if (this.before && typeof(this.before) == 'object') {
8325                 this.before = Roo.factory(this.before);
8326                 
8327                 inputblock.cn.push({
8328                     tag :'span',
8329                     cls : 'roo-input-before input-group-' +
8330                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8331                 });
8332             }
8333             
8334             inputblock.cn.push(input);
8335             
8336             if (this.after && typeof(this.after) == 'string') {
8337                 inputblock.cn.push({
8338                     tag :'span',
8339                     cls : 'roo-input-after input-group-addon',
8340                     html : this.after
8341                 });
8342             }
8343             if (this.after && typeof(this.after) == 'object') {
8344                 this.after = Roo.factory(this.after);
8345                 
8346                 inputblock.cn.push({
8347                     tag :'span',
8348                     cls : 'roo-input-after input-group-' +
8349                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8350                 });
8351             }
8352             
8353             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8354                 inputblock.cls += ' has-feedback';
8355                 inputblock.cn.push(feedback);
8356             }
8357         };
8358         
8359         if (align ==='left' && this.fieldLabel.length) {
8360             
8361             cfg.cn = [
8362                 {
8363                     tag : 'i',
8364                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8365                     tooltip : 'This field is required'
8366                 },
8367                 {
8368                     tag: 'label',
8369                     'for' :  id,
8370                     cls : 'control-label col-sm-' + this.labelWidth,
8371                     html : this.fieldLabel
8372
8373                 },
8374                 {
8375                     cls : "col-sm-" + (12 - this.labelWidth), 
8376                     cn: [
8377                         inputblock
8378                     ]
8379                 }
8380
8381             ];
8382             
8383             if(this.indicatorpos == 'right'){
8384                 cfg.cn = [
8385                     {
8386                         tag: 'label',
8387                         'for' :  id,
8388                         cls : 'control-label col-sm-' + this.labelWidth,
8389                         html : this.fieldLabel
8390
8391                     },
8392                     {
8393                         tag : 'i',
8394                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8395                         tooltip : 'This field is required'
8396                     },
8397                     {
8398                         cls : "col-sm-" + (12 - this.labelWidth), 
8399                         cn: [
8400                             inputblock
8401                         ]
8402                     }
8403
8404                 ];
8405             }
8406             
8407         } else if ( this.fieldLabel.length) {
8408                 
8409             cfg.cn = [
8410                 {
8411                     tag : 'i',
8412                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8413                     tooltip : 'This field is required'
8414                 },
8415                 {
8416                     tag: 'label',
8417                    //cls : 'input-group-addon',
8418                     html : this.fieldLabel
8419
8420                 },
8421
8422                inputblock
8423
8424            ];
8425            
8426            if(this.indicatorpos == 'right'){
8427                 
8428                 cfg.cn = [
8429                     {
8430                         tag: 'label',
8431                        //cls : 'input-group-addon',
8432                         html : this.fieldLabel
8433
8434                     },
8435                     {
8436                         tag : 'i',
8437                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8438                         tooltip : 'This field is required'
8439                     },
8440
8441                    inputblock
8442
8443                ];
8444
8445             }
8446
8447         } else {
8448             
8449             cfg.cn = [
8450
8451                     inputblock
8452
8453             ];
8454                 
8455                 
8456         };
8457         
8458         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8459            cfg.cls += ' navbar-form';
8460         }
8461         
8462         if (this.parentType === 'NavGroup') {
8463            cfg.cls += ' navbar-form';
8464            cfg.tag = 'li';
8465         }
8466         
8467         return cfg;
8468         
8469     },
8470     /**
8471      * return the real input element.
8472      */
8473     inputEl: function ()
8474     {
8475         return this.el.select('input.form-control',true).first();
8476     },
8477     
8478     tooltipEl : function()
8479     {
8480         return this.inputEl();
8481     },
8482     
8483     indicatorEl : function()
8484     {
8485         var indicator = this.el.select('i.roo-required-indicator',true).first();
8486         
8487         if(!indicator){
8488             return false;
8489         }
8490         
8491         return indicator;
8492         
8493     },
8494     
8495     setDisabled : function(v)
8496     {
8497         var i  = this.inputEl().dom;
8498         if (!v) {
8499             i.removeAttribute('disabled');
8500             return;
8501             
8502         }
8503         i.setAttribute('disabled','true');
8504     },
8505     initEvents : function()
8506     {
8507           
8508         this.inputEl().on("keydown" , this.fireKey,  this);
8509         this.inputEl().on("focus", this.onFocus,  this);
8510         this.inputEl().on("blur", this.onBlur,  this);
8511         
8512         this.inputEl().relayEvent('keyup', this);
8513         
8514         this.indicator = this.indicatorEl();
8515         
8516         if(this.indicator){
8517             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8518             this.indicator.hide();
8519         }
8520  
8521         // reference to original value for reset
8522         this.originalValue = this.getValue();
8523         //Roo.form.TextField.superclass.initEvents.call(this);
8524         if(this.validationEvent == 'keyup'){
8525             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8526             this.inputEl().on('keyup', this.filterValidation, this);
8527         }
8528         else if(this.validationEvent !== false){
8529             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8530         }
8531         
8532         if(this.selectOnFocus){
8533             this.on("focus", this.preFocus, this);
8534             
8535         }
8536         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8537             this.inputEl().on("keypress", this.filterKeys, this);
8538         }
8539        /* if(this.grow){
8540             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8541             this.el.on("click", this.autoSize,  this);
8542         }
8543         */
8544         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8545             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8546         }
8547         
8548         if (typeof(this.before) == 'object') {
8549             this.before.render(this.el.select('.roo-input-before',true).first());
8550         }
8551         if (typeof(this.after) == 'object') {
8552             this.after.render(this.el.select('.roo-input-after',true).first());
8553         }
8554         
8555         
8556     },
8557     filterValidation : function(e){
8558         if(!e.isNavKeyPress()){
8559             this.validationTask.delay(this.validationDelay);
8560         }
8561     },
8562      /**
8563      * Validates the field value
8564      * @return {Boolean} True if the value is valid, else false
8565      */
8566     validate : function(){
8567         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8568         if(this.disabled || this.validateValue(this.getRawValue())){
8569             this.markValid();
8570             return true;
8571         }
8572         
8573         this.markInvalid();
8574         return false;
8575     },
8576     
8577     
8578     /**
8579      * Validates a value according to the field's validation rules and marks the field as invalid
8580      * if the validation fails
8581      * @param {Mixed} value The value to validate
8582      * @return {Boolean} True if the value is valid, else false
8583      */
8584     validateValue : function(value){
8585         if(value.length < 1)  { // if it's blank
8586             if(this.allowBlank){
8587                 return true;
8588             }
8589             return false;
8590         }
8591         
8592         if(value.length < this.minLength){
8593             return false;
8594         }
8595         if(value.length > this.maxLength){
8596             return false;
8597         }
8598         if(this.vtype){
8599             var vt = Roo.form.VTypes;
8600             if(!vt[this.vtype](value, this)){
8601                 return false;
8602             }
8603         }
8604         if(typeof this.validator == "function"){
8605             var msg = this.validator(value);
8606             if(msg !== true){
8607                 return false;
8608             }
8609         }
8610         
8611         if(this.regex && !this.regex.test(value)){
8612             return false;
8613         }
8614         
8615         return true;
8616     },
8617
8618     
8619     
8620      // private
8621     fireKey : function(e){
8622         //Roo.log('field ' + e.getKey());
8623         if(e.isNavKeyPress()){
8624             this.fireEvent("specialkey", this, e);
8625         }
8626     },
8627     focus : function (selectText){
8628         if(this.rendered){
8629             this.inputEl().focus();
8630             if(selectText === true){
8631                 this.inputEl().dom.select();
8632             }
8633         }
8634         return this;
8635     } ,
8636     
8637     onFocus : function(){
8638         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8639            // this.el.addClass(this.focusClass);
8640         }
8641         if(!this.hasFocus){
8642             this.hasFocus = true;
8643             this.startValue = this.getValue();
8644             this.fireEvent("focus", this);
8645         }
8646     },
8647     
8648     beforeBlur : Roo.emptyFn,
8649
8650     
8651     // private
8652     onBlur : function(){
8653         this.beforeBlur();
8654         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8655             //this.el.removeClass(this.focusClass);
8656         }
8657         this.hasFocus = false;
8658         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8659             this.validate();
8660         }
8661         var v = this.getValue();
8662         if(String(v) !== String(this.startValue)){
8663             this.fireEvent('change', this, v, this.startValue);
8664         }
8665         this.fireEvent("blur", this);
8666     },
8667     
8668     /**
8669      * Resets the current field value to the originally loaded value and clears any validation messages
8670      */
8671     reset : function(){
8672         this.setValue(this.originalValue);
8673         this.validate();
8674     },
8675      /**
8676      * Returns the name of the field
8677      * @return {Mixed} name The name field
8678      */
8679     getName: function(){
8680         return this.name;
8681     },
8682      /**
8683      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8684      * @return {Mixed} value The field value
8685      */
8686     getValue : function(){
8687         
8688         var v = this.inputEl().getValue();
8689         
8690         return v;
8691     },
8692     /**
8693      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8694      * @return {Mixed} value The field value
8695      */
8696     getRawValue : function(){
8697         var v = this.inputEl().getValue();
8698         
8699         return v;
8700     },
8701     
8702     /**
8703      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8704      * @param {Mixed} value The value to set
8705      */
8706     setRawValue : function(v){
8707         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8708     },
8709     
8710     selectText : function(start, end){
8711         var v = this.getRawValue();
8712         if(v.length > 0){
8713             start = start === undefined ? 0 : start;
8714             end = end === undefined ? v.length : end;
8715             var d = this.inputEl().dom;
8716             if(d.setSelectionRange){
8717                 d.setSelectionRange(start, end);
8718             }else if(d.createTextRange){
8719                 var range = d.createTextRange();
8720                 range.moveStart("character", start);
8721                 range.moveEnd("character", v.length-end);
8722                 range.select();
8723             }
8724         }
8725     },
8726     
8727     /**
8728      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8729      * @param {Mixed} value The value to set
8730      */
8731     setValue : function(v){
8732         this.value = v;
8733         if(this.rendered){
8734             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8735             this.validate();
8736         }
8737     },
8738     
8739     /*
8740     processValue : function(value){
8741         if(this.stripCharsRe){
8742             var newValue = value.replace(this.stripCharsRe, '');
8743             if(newValue !== value){
8744                 this.setRawValue(newValue);
8745                 return newValue;
8746             }
8747         }
8748         return value;
8749     },
8750   */
8751     preFocus : function(){
8752         
8753         if(this.selectOnFocus){
8754             this.inputEl().dom.select();
8755         }
8756     },
8757     filterKeys : function(e){
8758         var k = e.getKey();
8759         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8760             return;
8761         }
8762         var c = e.getCharCode(), cc = String.fromCharCode(c);
8763         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8764             return;
8765         }
8766         if(!this.maskRe.test(cc)){
8767             e.stopEvent();
8768         }
8769     },
8770      /**
8771      * Clear any invalid styles/messages for this field
8772      */
8773     clearInvalid : function(){
8774         
8775         if(!this.el || this.preventMark){ // not rendered
8776             return;
8777         }
8778         
8779         if(this.indicator){
8780             this.indicator.hide();
8781         }
8782         
8783         this.el.removeClass(this.invalidClass);
8784         
8785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8786             
8787             var feedback = this.el.select('.form-control-feedback', true).first();
8788             
8789             if(feedback){
8790                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8791             }
8792             
8793         }
8794         
8795         this.fireEvent('valid', this);
8796     },
8797     
8798      /**
8799      * Mark this field as valid
8800      */
8801     markValid : function()
8802     {
8803         if(!this.el  || this.preventMark){ // not rendered
8804             return;
8805         }
8806         
8807         this.el.removeClass([this.invalidClass, this.validClass]);
8808         
8809         var feedback = this.el.select('.form-control-feedback', true).first();
8810             
8811         if(feedback){
8812             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8813         }
8814
8815         if(this.disabled || this.allowBlank){
8816             return;
8817         }
8818         
8819         if(this.indicator){
8820             this.indicator.hide();
8821         }
8822         
8823         this.el.addClass(this.validClass);
8824         
8825         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8826             
8827             var feedback = this.el.select('.form-control-feedback', true).first();
8828             
8829             if(feedback){
8830                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8831                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8832             }
8833             
8834         }
8835         
8836         this.fireEvent('valid', this);
8837     },
8838     
8839      /**
8840      * Mark this field as invalid
8841      * @param {String} msg The validation message
8842      */
8843     markInvalid : function(msg)
8844     {
8845         if(!this.el  || this.preventMark){ // not rendered
8846             return;
8847         }
8848         
8849         this.el.removeClass([this.invalidClass, this.validClass]);
8850         
8851         var feedback = this.el.select('.form-control-feedback', true).first();
8852             
8853         if(feedback){
8854             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8855         }
8856
8857         if(this.disabled || this.allowBlank){
8858             return;
8859         }
8860         
8861         if(this.indicator){
8862             this.indicator.show();
8863         }
8864         
8865         this.el.addClass(this.invalidClass);
8866         
8867         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8868             
8869             var feedback = this.el.select('.form-control-feedback', true).first();
8870             
8871             if(feedback){
8872                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8873                 
8874                 if(this.getValue().length || this.forceFeedback){
8875                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8876                 }
8877                 
8878             }
8879             
8880         }
8881         
8882         this.fireEvent('invalid', this, msg);
8883     },
8884     // private
8885     SafariOnKeyDown : function(event)
8886     {
8887         // this is a workaround for a password hang bug on chrome/ webkit.
8888         
8889         var isSelectAll = false;
8890         
8891         if(this.inputEl().dom.selectionEnd > 0){
8892             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8893         }
8894         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8895             event.preventDefault();
8896             this.setValue('');
8897             return;
8898         }
8899         
8900         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8901             
8902             event.preventDefault();
8903             // this is very hacky as keydown always get's upper case.
8904             //
8905             var cc = String.fromCharCode(event.getCharCode());
8906             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8907             
8908         }
8909     },
8910     adjustWidth : function(tag, w){
8911         tag = tag.toLowerCase();
8912         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8913             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8914                 if(tag == 'input'){
8915                     return w + 2;
8916                 }
8917                 if(tag == 'textarea'){
8918                     return w-2;
8919                 }
8920             }else if(Roo.isOpera){
8921                 if(tag == 'input'){
8922                     return w + 2;
8923                 }
8924                 if(tag == 'textarea'){
8925                     return w-2;
8926                 }
8927             }
8928         }
8929         return w;
8930     }
8931     
8932 });
8933
8934  
8935 /*
8936  * - LGPL
8937  *
8938  * Input
8939  * 
8940  */
8941
8942 /**
8943  * @class Roo.bootstrap.TextArea
8944  * @extends Roo.bootstrap.Input
8945  * Bootstrap TextArea class
8946  * @cfg {Number} cols Specifies the visible width of a text area
8947  * @cfg {Number} rows Specifies the visible number of lines in a text area
8948  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8949  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8950  * @cfg {string} html text
8951  * 
8952  * @constructor
8953  * Create a new TextArea
8954  * @param {Object} config The config object
8955  */
8956
8957 Roo.bootstrap.TextArea = function(config){
8958     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8959    
8960 };
8961
8962 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8963      
8964     cols : false,
8965     rows : 5,
8966     readOnly : false,
8967     warp : 'soft',
8968     resize : false,
8969     value: false,
8970     html: false,
8971     
8972     getAutoCreate : function(){
8973         
8974         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8975         
8976         var id = Roo.id();
8977         
8978         var cfg = {};
8979         
8980         var input =  {
8981             tag: 'textarea',
8982             id : id,
8983             warp : this.warp,
8984             rows : this.rows,
8985             value : this.value || '',
8986             html: this.html || '',
8987             cls : 'form-control',
8988             placeholder : this.placeholder || '' 
8989             
8990         };
8991         
8992         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8993             input.maxLength = this.maxLength;
8994         }
8995         
8996         if(this.resize){
8997             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8998         }
8999         
9000         if(this.cols){
9001             input.cols = this.cols;
9002         }
9003         
9004         if (this.readOnly) {
9005             input.readonly = true;
9006         }
9007         
9008         if (this.name) {
9009             input.name = this.name;
9010         }
9011         
9012         if (this.size) {
9013             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9014         }
9015         
9016         var settings=this;
9017         ['xs','sm','md','lg'].map(function(size){
9018             if (settings[size]) {
9019                 cfg.cls += ' col-' + size + '-' + settings[size];
9020             }
9021         });
9022         
9023         var inputblock = input;
9024         
9025         if(this.hasFeedback && !this.allowBlank){
9026             
9027             var feedback = {
9028                 tag: 'span',
9029                 cls: 'glyphicon form-control-feedback'
9030             };
9031
9032             inputblock = {
9033                 cls : 'has-feedback',
9034                 cn :  [
9035                     input,
9036                     feedback
9037                 ] 
9038             };  
9039         }
9040         
9041         
9042         if (this.before || this.after) {
9043             
9044             inputblock = {
9045                 cls : 'input-group',
9046                 cn :  [] 
9047             };
9048             if (this.before) {
9049                 inputblock.cn.push({
9050                     tag :'span',
9051                     cls : 'input-group-addon',
9052                     html : this.before
9053                 });
9054             }
9055             
9056             inputblock.cn.push(input);
9057             
9058             if(this.hasFeedback && !this.allowBlank){
9059                 inputblock.cls += ' has-feedback';
9060                 inputblock.cn.push(feedback);
9061             }
9062             
9063             if (this.after) {
9064                 inputblock.cn.push({
9065                     tag :'span',
9066                     cls : 'input-group-addon',
9067                     html : this.after
9068                 });
9069             }
9070             
9071         }
9072         
9073         if (align ==='left' && this.fieldLabel.length) {
9074 //                Roo.log("left and has label");
9075                 cfg.cn = [
9076                     
9077                     {
9078                         tag: 'label',
9079                         'for' :  id,
9080                         cls : 'control-label col-sm-' + this.labelWidth,
9081                         html : this.fieldLabel
9082                         
9083                     },
9084                     {
9085                         cls : "col-sm-" + (12 - this.labelWidth), 
9086                         cn: [
9087                             inputblock
9088                         ]
9089                     }
9090                     
9091                 ];
9092         } else if ( this.fieldLabel.length) {
9093 //                Roo.log(" label");
9094                  cfg.cn = [
9095                    
9096                     {
9097                         tag: 'label',
9098                         //cls : 'input-group-addon',
9099                         html : this.fieldLabel
9100                         
9101                     },
9102                     
9103                     inputblock
9104                     
9105                 ];
9106
9107         } else {
9108             
9109 //                   Roo.log(" no label && no align");
9110                 cfg.cn = [
9111                     
9112                         inputblock
9113                     
9114                 ];
9115                 
9116                 
9117         }
9118         
9119         if (this.disabled) {
9120             input.disabled=true;
9121         }
9122         
9123         return cfg;
9124         
9125     },
9126     /**
9127      * return the real textarea element.
9128      */
9129     inputEl: function ()
9130     {
9131         return this.el.select('textarea.form-control',true).first();
9132     },
9133     
9134     /**
9135      * Clear any invalid styles/messages for this field
9136      */
9137     clearInvalid : function()
9138     {
9139         
9140         if(!this.el || this.preventMark){ // not rendered
9141             return;
9142         }
9143         
9144         var label = this.el.select('label', true).first();
9145         var icon = this.el.select('i.fa-star', true).first();
9146         
9147         if(label && icon){
9148             icon.remove();
9149         }
9150         
9151         this.el.removeClass(this.invalidClass);
9152         
9153         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9154             
9155             var feedback = this.el.select('.form-control-feedback', true).first();
9156             
9157             if(feedback){
9158                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9159             }
9160             
9161         }
9162         
9163         this.fireEvent('valid', this);
9164     },
9165     
9166      /**
9167      * Mark this field as valid
9168      */
9169     markValid : function()
9170     {
9171         if(!this.el  || this.preventMark){ // not rendered
9172             return;
9173         }
9174         
9175         this.el.removeClass([this.invalidClass, this.validClass]);
9176         
9177         var feedback = this.el.select('.form-control-feedback', true).first();
9178             
9179         if(feedback){
9180             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9181         }
9182
9183         if(this.disabled || this.allowBlank){
9184             return;
9185         }
9186         
9187         var label = this.el.select('label', true).first();
9188         var icon = this.el.select('i.fa-star', true).first();
9189         
9190         if(label && icon){
9191             icon.remove();
9192         }
9193         
9194         this.el.addClass(this.validClass);
9195         
9196         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9197             
9198             var feedback = this.el.select('.form-control-feedback', true).first();
9199             
9200             if(feedback){
9201                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9202                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9203             }
9204             
9205         }
9206         
9207         this.fireEvent('valid', this);
9208     },
9209     
9210      /**
9211      * Mark this field as invalid
9212      * @param {String} msg The validation message
9213      */
9214     markInvalid : function(msg)
9215     {
9216         if(!this.el  || this.preventMark){ // not rendered
9217             return;
9218         }
9219         
9220         this.el.removeClass([this.invalidClass, this.validClass]);
9221         
9222         var feedback = this.el.select('.form-control-feedback', true).first();
9223             
9224         if(feedback){
9225             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9226         }
9227
9228         if(this.disabled || this.allowBlank){
9229             return;
9230         }
9231         
9232         var label = this.el.select('label', true).first();
9233         var icon = this.el.select('i.fa-star', true).first();
9234         
9235         if(!this.getValue().length && label && !icon){
9236             this.el.createChild({
9237                 tag : 'i',
9238                 cls : 'text-danger fa fa-lg fa-star',
9239                 tooltip : 'This field is required',
9240                 style : 'margin-right:5px;'
9241             }, label, true);
9242         }
9243
9244         this.el.addClass(this.invalidClass);
9245         
9246         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9247             
9248             var feedback = this.el.select('.form-control-feedback', true).first();
9249             
9250             if(feedback){
9251                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9252                 
9253                 if(this.getValue().length || this.forceFeedback){
9254                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9255                 }
9256                 
9257             }
9258             
9259         }
9260         
9261         this.fireEvent('invalid', this, msg);
9262     }
9263 });
9264
9265  
9266 /*
9267  * - LGPL
9268  *
9269  * trigger field - base class for combo..
9270  * 
9271  */
9272  
9273 /**
9274  * @class Roo.bootstrap.TriggerField
9275  * @extends Roo.bootstrap.Input
9276  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9277  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9278  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9279  * for which you can provide a custom implementation.  For example:
9280  * <pre><code>
9281 var trigger = new Roo.bootstrap.TriggerField();
9282 trigger.onTriggerClick = myTriggerFn;
9283 trigger.applyTo('my-field');
9284 </code></pre>
9285  *
9286  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9287  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9288  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9289  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9290  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9291
9292  * @constructor
9293  * Create a new TriggerField.
9294  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9295  * to the base TextField)
9296  */
9297 Roo.bootstrap.TriggerField = function(config){
9298     this.mimicing = false;
9299     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9300 };
9301
9302 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9303     /**
9304      * @cfg {String} triggerClass A CSS class to apply to the trigger
9305      */
9306      /**
9307      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9308      */
9309     hideTrigger:false,
9310
9311     /**
9312      * @cfg {Boolean} removable (true|false) special filter default false
9313      */
9314     removable : false,
9315     
9316     /** @cfg {Boolean} grow @hide */
9317     /** @cfg {Number} growMin @hide */
9318     /** @cfg {Number} growMax @hide */
9319
9320     /**
9321      * @hide 
9322      * @method
9323      */
9324     autoSize: Roo.emptyFn,
9325     // private
9326     monitorTab : true,
9327     // private
9328     deferHeight : true,
9329
9330     
9331     actionMode : 'wrap',
9332     
9333     caret : false,
9334     
9335     
9336     getAutoCreate : function(){
9337        
9338         var align = this.labelAlign || this.parentLabelAlign();
9339         
9340         var id = Roo.id();
9341         
9342         var cfg = {
9343             cls: 'form-group' //input-group
9344         };
9345         
9346         
9347         var input =  {
9348             tag: 'input',
9349             id : id,
9350             type : this.inputType,
9351             cls : 'form-control',
9352             autocomplete: 'new-password',
9353             placeholder : this.placeholder || '' 
9354             
9355         };
9356         if (this.name) {
9357             input.name = this.name;
9358         }
9359         if (this.size) {
9360             input.cls += ' input-' + this.size;
9361         }
9362         
9363         if (this.disabled) {
9364             input.disabled=true;
9365         }
9366         
9367         var inputblock = input;
9368         
9369         if(this.hasFeedback && !this.allowBlank){
9370             
9371             var feedback = {
9372                 tag: 'span',
9373                 cls: 'glyphicon form-control-feedback'
9374             };
9375             
9376             if(this.removable && !this.editable && !this.tickable){
9377                 inputblock = {
9378                     cls : 'has-feedback',
9379                     cn :  [
9380                         inputblock,
9381                         {
9382                             tag: 'button',
9383                             html : 'x',
9384                             cls : 'roo-combo-removable-btn close'
9385                         },
9386                         feedback
9387                     ] 
9388                 };
9389             } else {
9390                 inputblock = {
9391                     cls : 'has-feedback',
9392                     cn :  [
9393                         inputblock,
9394                         feedback
9395                     ] 
9396                 };
9397             }
9398
9399         } else {
9400             if(this.removable && !this.editable && !this.tickable){
9401                 inputblock = {
9402                     cls : 'roo-removable',
9403                     cn :  [
9404                         inputblock,
9405                         {
9406                             tag: 'button',
9407                             html : 'x',
9408                             cls : 'roo-combo-removable-btn close'
9409                         }
9410                     ] 
9411                 };
9412             }
9413         }
9414         
9415         if (this.before || this.after) {
9416             
9417             inputblock = {
9418                 cls : 'input-group',
9419                 cn :  [] 
9420             };
9421             if (this.before) {
9422                 inputblock.cn.push({
9423                     tag :'span',
9424                     cls : 'input-group-addon',
9425                     html : this.before
9426                 });
9427             }
9428             
9429             inputblock.cn.push(input);
9430             
9431             if(this.hasFeedback && !this.allowBlank){
9432                 inputblock.cls += ' has-feedback';
9433                 inputblock.cn.push(feedback);
9434             }
9435             
9436             if (this.after) {
9437                 inputblock.cn.push({
9438                     tag :'span',
9439                     cls : 'input-group-addon',
9440                     html : this.after
9441                 });
9442             }
9443             
9444         };
9445         
9446         var box = {
9447             tag: 'div',
9448             cn: [
9449                 {
9450                     tag: 'input',
9451                     type : 'hidden',
9452                     cls: 'form-hidden-field'
9453                 },
9454                 inputblock
9455             ]
9456             
9457         };
9458         
9459         if(this.multiple){
9460             box = {
9461                 tag: 'div',
9462                 cn: [
9463                     {
9464                         tag: 'input',
9465                         type : 'hidden',
9466                         cls: 'form-hidden-field'
9467                     },
9468                     {
9469                         tag: 'ul',
9470                         cls: 'roo-select2-choices',
9471                         cn:[
9472                             {
9473                                 tag: 'li',
9474                                 cls: 'roo-select2-search-field',
9475                                 cn: [
9476
9477                                     inputblock
9478                                 ]
9479                             }
9480                         ]
9481                     }
9482                 ]
9483             }
9484         };
9485         
9486         var combobox = {
9487             cls: 'roo-select2-container input-group',
9488             cn: [
9489                 box
9490 //                {
9491 //                    tag: 'ul',
9492 //                    cls: 'typeahead typeahead-long dropdown-menu',
9493 //                    style: 'display:none'
9494 //                }
9495             ]
9496         };
9497         
9498         if(!this.multiple && this.showToggleBtn){
9499             
9500             var caret = {
9501                         tag: 'span',
9502                         cls: 'caret'
9503              };
9504             if (this.caret != false) {
9505                 caret = {
9506                      tag: 'i',
9507                      cls: 'fa fa-' + this.caret
9508                 };
9509                 
9510             }
9511             
9512             combobox.cn.push({
9513                 tag :'span',
9514                 cls : 'input-group-addon btn dropdown-toggle',
9515                 cn : [
9516                     caret,
9517                     {
9518                         tag: 'span',
9519                         cls: 'combobox-clear',
9520                         cn  : [
9521                             {
9522                                 tag : 'i',
9523                                 cls: 'icon-remove'
9524                             }
9525                         ]
9526                     }
9527                 ]
9528
9529             })
9530         }
9531         
9532         if(this.multiple){
9533             combobox.cls += ' roo-select2-container-multi';
9534         }
9535         
9536         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9537             
9538 //                Roo.log("left and has label");
9539             cfg.cn = [
9540                 {
9541                     tag : 'i',
9542                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9543                     tooltip : 'This field is required'
9544                 },
9545                 {
9546                     tag: 'label',
9547                     'for' :  id,
9548                     cls : 'control-label col-sm-' + this.labelWidth,
9549                     html : this.fieldLabel
9550
9551                 },
9552                 {
9553                     cls : "col-sm-" + (12 - this.labelWidth), 
9554                     cn: [
9555                         combobox
9556                     ]
9557                 }
9558
9559             ];
9560             
9561             if(this.indicatorpos == 'right'){
9562                 cfg.cn = [
9563                     {
9564                         tag: 'label',
9565                         'for' :  id,
9566                         cls : 'control-label col-sm-' + this.labelWidth,
9567                         html : this.fieldLabel
9568
9569                     },
9570                     {
9571                         tag : 'i',
9572                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9573                         tooltip : 'This field is required'
9574                     },
9575                     {
9576                         cls : "col-sm-" + (12 - this.labelWidth), 
9577                         cn: [
9578                             combobox
9579                         ]
9580                     }
9581
9582                 ];
9583             }
9584             
9585         } else if ( this.fieldLabel.length) {
9586 //                Roo.log(" label");
9587             cfg.cn = [
9588                 {
9589                    tag : 'i',
9590                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9591                    tooltip : 'This field is required'
9592                },
9593                {
9594                    tag: 'label',
9595                    //cls : 'input-group-addon',
9596                    html : this.fieldLabel
9597
9598                },
9599
9600                combobox
9601
9602             ];
9603             
9604             if(this.indicatorpos == 'right'){
9605                 
9606                 cfg.cn = [
9607                     {
9608                        tag: 'label',
9609                        //cls : 'input-group-addon',
9610                        html : this.fieldLabel
9611
9612                     },
9613                     {
9614                        tag : 'i',
9615                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9616                        tooltip : 'This field is required'
9617                     },
9618                     
9619                     combobox
9620
9621                 ];
9622
9623             }
9624
9625         } else {
9626             
9627 //                Roo.log(" no label && no align");
9628                 cfg = combobox
9629                      
9630                 
9631         }
9632          
9633         var settings=this;
9634         ['xs','sm','md','lg'].map(function(size){
9635             if (settings[size]) {
9636                 cfg.cls += ' col-' + size + '-' + settings[size];
9637             }
9638         });
9639         
9640         return cfg;
9641         
9642     },
9643     
9644     
9645     
9646     // private
9647     onResize : function(w, h){
9648 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9649 //        if(typeof w == 'number'){
9650 //            var x = w - this.trigger.getWidth();
9651 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9652 //            this.trigger.setStyle('left', x+'px');
9653 //        }
9654     },
9655
9656     // private
9657     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9658
9659     // private
9660     getResizeEl : function(){
9661         return this.inputEl();
9662     },
9663
9664     // private
9665     getPositionEl : function(){
9666         return this.inputEl();
9667     },
9668
9669     // private
9670     alignErrorIcon : function(){
9671         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9672     },
9673
9674     // private
9675     initEvents : function(){
9676         
9677         this.createList();
9678         
9679         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9680         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9681         if(!this.multiple && this.showToggleBtn){
9682             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9683             if(this.hideTrigger){
9684                 this.trigger.setDisplayed(false);
9685             }
9686             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9687         }
9688         
9689         if(this.multiple){
9690             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9691         }
9692         
9693         if(this.removable && !this.editable && !this.tickable){
9694             var close = this.closeTriggerEl();
9695             
9696             if(close){
9697                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9698                 close.on('click', this.removeBtnClick, this, close);
9699             }
9700         }
9701         
9702         //this.trigger.addClassOnOver('x-form-trigger-over');
9703         //this.trigger.addClassOnClick('x-form-trigger-click');
9704         
9705         //if(!this.width){
9706         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9707         //}
9708     },
9709     
9710     closeTriggerEl : function()
9711     {
9712         var close = this.el.select('.roo-combo-removable-btn', true).first();
9713         return close ? close : false;
9714     },
9715     
9716     removeBtnClick : function(e, h, el)
9717     {
9718         e.preventDefault();
9719         
9720         if(this.fireEvent("remove", this) !== false){
9721             this.reset();
9722             this.fireEvent("afterremove", this)
9723         }
9724     },
9725     
9726     createList : function()
9727     {
9728         this.list = Roo.get(document.body).createChild({
9729             tag: 'ul',
9730             cls: 'typeahead typeahead-long dropdown-menu',
9731             style: 'display:none'
9732         });
9733         
9734         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9735         
9736     },
9737
9738     // private
9739     initTrigger : function(){
9740        
9741     },
9742
9743     // private
9744     onDestroy : function(){
9745         if(this.trigger){
9746             this.trigger.removeAllListeners();
9747           //  this.trigger.remove();
9748         }
9749         //if(this.wrap){
9750         //    this.wrap.remove();
9751         //}
9752         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9753     },
9754
9755     // private
9756     onFocus : function(){
9757         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9758         /*
9759         if(!this.mimicing){
9760             this.wrap.addClass('x-trigger-wrap-focus');
9761             this.mimicing = true;
9762             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9763             if(this.monitorTab){
9764                 this.el.on("keydown", this.checkTab, this);
9765             }
9766         }
9767         */
9768     },
9769
9770     // private
9771     checkTab : function(e){
9772         if(e.getKey() == e.TAB){
9773             this.triggerBlur();
9774         }
9775     },
9776
9777     // private
9778     onBlur : function(){
9779         // do nothing
9780     },
9781
9782     // private
9783     mimicBlur : function(e, t){
9784         /*
9785         if(!this.wrap.contains(t) && this.validateBlur()){
9786             this.triggerBlur();
9787         }
9788         */
9789     },
9790
9791     // private
9792     triggerBlur : function(){
9793         this.mimicing = false;
9794         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9795         if(this.monitorTab){
9796             this.el.un("keydown", this.checkTab, this);
9797         }
9798         //this.wrap.removeClass('x-trigger-wrap-focus');
9799         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9800     },
9801
9802     // private
9803     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9804     validateBlur : function(e, t){
9805         return true;
9806     },
9807
9808     // private
9809     onDisable : function(){
9810         this.inputEl().dom.disabled = true;
9811         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9812         //if(this.wrap){
9813         //    this.wrap.addClass('x-item-disabled');
9814         //}
9815     },
9816
9817     // private
9818     onEnable : function(){
9819         this.inputEl().dom.disabled = false;
9820         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9821         //if(this.wrap){
9822         //    this.el.removeClass('x-item-disabled');
9823         //}
9824     },
9825
9826     // private
9827     onShow : function(){
9828         var ae = this.getActionEl();
9829         
9830         if(ae){
9831             ae.dom.style.display = '';
9832             ae.dom.style.visibility = 'visible';
9833         }
9834     },
9835
9836     // private
9837     
9838     onHide : function(){
9839         var ae = this.getActionEl();
9840         ae.dom.style.display = 'none';
9841     },
9842
9843     /**
9844      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9845      * by an implementing function.
9846      * @method
9847      * @param {EventObject} e
9848      */
9849     onTriggerClick : Roo.emptyFn
9850 });
9851  /*
9852  * Based on:
9853  * Ext JS Library 1.1.1
9854  * Copyright(c) 2006-2007, Ext JS, LLC.
9855  *
9856  * Originally Released Under LGPL - original licence link has changed is not relivant.
9857  *
9858  * Fork - LGPL
9859  * <script type="text/javascript">
9860  */
9861
9862
9863 /**
9864  * @class Roo.data.SortTypes
9865  * @singleton
9866  * Defines the default sorting (casting?) comparison functions used when sorting data.
9867  */
9868 Roo.data.SortTypes = {
9869     /**
9870      * Default sort that does nothing
9871      * @param {Mixed} s The value being converted
9872      * @return {Mixed} The comparison value
9873      */
9874     none : function(s){
9875         return s;
9876     },
9877     
9878     /**
9879      * The regular expression used to strip tags
9880      * @type {RegExp}
9881      * @property
9882      */
9883     stripTagsRE : /<\/?[^>]+>/gi,
9884     
9885     /**
9886      * Strips all HTML tags to sort on text only
9887      * @param {Mixed} s The value being converted
9888      * @return {String} The comparison value
9889      */
9890     asText : function(s){
9891         return String(s).replace(this.stripTagsRE, "");
9892     },
9893     
9894     /**
9895      * Strips all HTML tags to sort on text only - Case insensitive
9896      * @param {Mixed} s The value being converted
9897      * @return {String} The comparison value
9898      */
9899     asUCText : function(s){
9900         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9901     },
9902     
9903     /**
9904      * Case insensitive string
9905      * @param {Mixed} s The value being converted
9906      * @return {String} The comparison value
9907      */
9908     asUCString : function(s) {
9909         return String(s).toUpperCase();
9910     },
9911     
9912     /**
9913      * Date sorting
9914      * @param {Mixed} s The value being converted
9915      * @return {Number} The comparison value
9916      */
9917     asDate : function(s) {
9918         if(!s){
9919             return 0;
9920         }
9921         if(s instanceof Date){
9922             return s.getTime();
9923         }
9924         return Date.parse(String(s));
9925     },
9926     
9927     /**
9928      * Float sorting
9929      * @param {Mixed} s The value being converted
9930      * @return {Float} The comparison value
9931      */
9932     asFloat : function(s) {
9933         var val = parseFloat(String(s).replace(/,/g, ""));
9934         if(isNaN(val)) {
9935             val = 0;
9936         }
9937         return val;
9938     },
9939     
9940     /**
9941      * Integer sorting
9942      * @param {Mixed} s The value being converted
9943      * @return {Number} The comparison value
9944      */
9945     asInt : function(s) {
9946         var val = parseInt(String(s).replace(/,/g, ""));
9947         if(isNaN(val)) {
9948             val = 0;
9949         }
9950         return val;
9951     }
9952 };/*
9953  * Based on:
9954  * Ext JS Library 1.1.1
9955  * Copyright(c) 2006-2007, Ext JS, LLC.
9956  *
9957  * Originally Released Under LGPL - original licence link has changed is not relivant.
9958  *
9959  * Fork - LGPL
9960  * <script type="text/javascript">
9961  */
9962
9963 /**
9964 * @class Roo.data.Record
9965  * Instances of this class encapsulate both record <em>definition</em> information, and record
9966  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9967  * to access Records cached in an {@link Roo.data.Store} object.<br>
9968  * <p>
9969  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9970  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9971  * objects.<br>
9972  * <p>
9973  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9974  * @constructor
9975  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9976  * {@link #create}. The parameters are the same.
9977  * @param {Array} data An associative Array of data values keyed by the field name.
9978  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9979  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9980  * not specified an integer id is generated.
9981  */
9982 Roo.data.Record = function(data, id){
9983     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9984     this.data = data;
9985 };
9986
9987 /**
9988  * Generate a constructor for a specific record layout.
9989  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9990  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9991  * Each field definition object may contain the following properties: <ul>
9992  * <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,
9993  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9994  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9995  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9996  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9997  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9998  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9999  * this may be omitted.</p></li>
10000  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10001  * <ul><li>auto (Default, implies no conversion)</li>
10002  * <li>string</li>
10003  * <li>int</li>
10004  * <li>float</li>
10005  * <li>boolean</li>
10006  * <li>date</li></ul></p></li>
10007  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10008  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10009  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10010  * by the Reader into an object that will be stored in the Record. It is passed the
10011  * following parameters:<ul>
10012  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10013  * </ul></p></li>
10014  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10015  * </ul>
10016  * <br>usage:<br><pre><code>
10017 var TopicRecord = Roo.data.Record.create(
10018     {name: 'title', mapping: 'topic_title'},
10019     {name: 'author', mapping: 'username'},
10020     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10021     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10022     {name: 'lastPoster', mapping: 'user2'},
10023     {name: 'excerpt', mapping: 'post_text'}
10024 );
10025
10026 var myNewRecord = new TopicRecord({
10027     title: 'Do my job please',
10028     author: 'noobie',
10029     totalPosts: 1,
10030     lastPost: new Date(),
10031     lastPoster: 'Animal',
10032     excerpt: 'No way dude!'
10033 });
10034 myStore.add(myNewRecord);
10035 </code></pre>
10036  * @method create
10037  * @static
10038  */
10039 Roo.data.Record.create = function(o){
10040     var f = function(){
10041         f.superclass.constructor.apply(this, arguments);
10042     };
10043     Roo.extend(f, Roo.data.Record);
10044     var p = f.prototype;
10045     p.fields = new Roo.util.MixedCollection(false, function(field){
10046         return field.name;
10047     });
10048     for(var i = 0, len = o.length; i < len; i++){
10049         p.fields.add(new Roo.data.Field(o[i]));
10050     }
10051     f.getField = function(name){
10052         return p.fields.get(name);  
10053     };
10054     return f;
10055 };
10056
10057 Roo.data.Record.AUTO_ID = 1000;
10058 Roo.data.Record.EDIT = 'edit';
10059 Roo.data.Record.REJECT = 'reject';
10060 Roo.data.Record.COMMIT = 'commit';
10061
10062 Roo.data.Record.prototype = {
10063     /**
10064      * Readonly flag - true if this record has been modified.
10065      * @type Boolean
10066      */
10067     dirty : false,
10068     editing : false,
10069     error: null,
10070     modified: null,
10071
10072     // private
10073     join : function(store){
10074         this.store = store;
10075     },
10076
10077     /**
10078      * Set the named field to the specified value.
10079      * @param {String} name The name of the field to set.
10080      * @param {Object} value The value to set the field to.
10081      */
10082     set : function(name, value){
10083         if(this.data[name] == value){
10084             return;
10085         }
10086         this.dirty = true;
10087         if(!this.modified){
10088             this.modified = {};
10089         }
10090         if(typeof this.modified[name] == 'undefined'){
10091             this.modified[name] = this.data[name];
10092         }
10093         this.data[name] = value;
10094         if(!this.editing && this.store){
10095             this.store.afterEdit(this);
10096         }       
10097     },
10098
10099     /**
10100      * Get the value of the named field.
10101      * @param {String} name The name of the field to get the value of.
10102      * @return {Object} The value of the field.
10103      */
10104     get : function(name){
10105         return this.data[name]; 
10106     },
10107
10108     // private
10109     beginEdit : function(){
10110         this.editing = true;
10111         this.modified = {}; 
10112     },
10113
10114     // private
10115     cancelEdit : function(){
10116         this.editing = false;
10117         delete this.modified;
10118     },
10119
10120     // private
10121     endEdit : function(){
10122         this.editing = false;
10123         if(this.dirty && this.store){
10124             this.store.afterEdit(this);
10125         }
10126     },
10127
10128     /**
10129      * Usually called by the {@link Roo.data.Store} which owns the Record.
10130      * Rejects all changes made to the Record since either creation, or the last commit operation.
10131      * Modified fields are reverted to their original values.
10132      * <p>
10133      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10134      * of reject operations.
10135      */
10136     reject : function(){
10137         var m = this.modified;
10138         for(var n in m){
10139             if(typeof m[n] != "function"){
10140                 this.data[n] = m[n];
10141             }
10142         }
10143         this.dirty = false;
10144         delete this.modified;
10145         this.editing = false;
10146         if(this.store){
10147             this.store.afterReject(this);
10148         }
10149     },
10150
10151     /**
10152      * Usually called by the {@link Roo.data.Store} which owns the Record.
10153      * Commits all changes made to the Record since either creation, or the last commit operation.
10154      * <p>
10155      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10156      * of commit operations.
10157      */
10158     commit : function(){
10159         this.dirty = false;
10160         delete this.modified;
10161         this.editing = false;
10162         if(this.store){
10163             this.store.afterCommit(this);
10164         }
10165     },
10166
10167     // private
10168     hasError : function(){
10169         return this.error != null;
10170     },
10171
10172     // private
10173     clearError : function(){
10174         this.error = null;
10175     },
10176
10177     /**
10178      * Creates a copy of this record.
10179      * @param {String} id (optional) A new record id if you don't want to use this record's id
10180      * @return {Record}
10181      */
10182     copy : function(newId) {
10183         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10184     }
10185 };/*
10186  * Based on:
10187  * Ext JS Library 1.1.1
10188  * Copyright(c) 2006-2007, Ext JS, LLC.
10189  *
10190  * Originally Released Under LGPL - original licence link has changed is not relivant.
10191  *
10192  * Fork - LGPL
10193  * <script type="text/javascript">
10194  */
10195
10196
10197
10198 /**
10199  * @class Roo.data.Store
10200  * @extends Roo.util.Observable
10201  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10202  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10203  * <p>
10204  * 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
10205  * has no knowledge of the format of the data returned by the Proxy.<br>
10206  * <p>
10207  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10208  * instances from the data object. These records are cached and made available through accessor functions.
10209  * @constructor
10210  * Creates a new Store.
10211  * @param {Object} config A config object containing the objects needed for the Store to access data,
10212  * and read the data into Records.
10213  */
10214 Roo.data.Store = function(config){
10215     this.data = new Roo.util.MixedCollection(false);
10216     this.data.getKey = function(o){
10217         return o.id;
10218     };
10219     this.baseParams = {};
10220     // private
10221     this.paramNames = {
10222         "start" : "start",
10223         "limit" : "limit",
10224         "sort" : "sort",
10225         "dir" : "dir",
10226         "multisort" : "_multisort"
10227     };
10228
10229     if(config && config.data){
10230         this.inlineData = config.data;
10231         delete config.data;
10232     }
10233
10234     Roo.apply(this, config);
10235     
10236     if(this.reader){ // reader passed
10237         this.reader = Roo.factory(this.reader, Roo.data);
10238         this.reader.xmodule = this.xmodule || false;
10239         if(!this.recordType){
10240             this.recordType = this.reader.recordType;
10241         }
10242         if(this.reader.onMetaChange){
10243             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10244         }
10245     }
10246
10247     if(this.recordType){
10248         this.fields = this.recordType.prototype.fields;
10249     }
10250     this.modified = [];
10251
10252     this.addEvents({
10253         /**
10254          * @event datachanged
10255          * Fires when the data cache has changed, and a widget which is using this Store
10256          * as a Record cache should refresh its view.
10257          * @param {Store} this
10258          */
10259         datachanged : true,
10260         /**
10261          * @event metachange
10262          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10263          * @param {Store} this
10264          * @param {Object} meta The JSON metadata
10265          */
10266         metachange : true,
10267         /**
10268          * @event add
10269          * Fires when Records have been added to the Store
10270          * @param {Store} this
10271          * @param {Roo.data.Record[]} records The array of Records added
10272          * @param {Number} index The index at which the record(s) were added
10273          */
10274         add : true,
10275         /**
10276          * @event remove
10277          * Fires when a Record has been removed from the Store
10278          * @param {Store} this
10279          * @param {Roo.data.Record} record The Record that was removed
10280          * @param {Number} index The index at which the record was removed
10281          */
10282         remove : true,
10283         /**
10284          * @event update
10285          * Fires when a Record has been updated
10286          * @param {Store} this
10287          * @param {Roo.data.Record} record The Record that was updated
10288          * @param {String} operation The update operation being performed.  Value may be one of:
10289          * <pre><code>
10290  Roo.data.Record.EDIT
10291  Roo.data.Record.REJECT
10292  Roo.data.Record.COMMIT
10293          * </code></pre>
10294          */
10295         update : true,
10296         /**
10297          * @event clear
10298          * Fires when the data cache has been cleared.
10299          * @param {Store} this
10300          */
10301         clear : true,
10302         /**
10303          * @event beforeload
10304          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10305          * the load action will be canceled.
10306          * @param {Store} this
10307          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10308          */
10309         beforeload : true,
10310         /**
10311          * @event beforeloadadd
10312          * Fires after a new set of Records has been loaded.
10313          * @param {Store} this
10314          * @param {Roo.data.Record[]} records The Records that were loaded
10315          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10316          */
10317         beforeloadadd : true,
10318         /**
10319          * @event load
10320          * Fires after a new set of Records has been loaded, before they are added to the store.
10321          * @param {Store} this
10322          * @param {Roo.data.Record[]} records The Records that were loaded
10323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10324          * @params {Object} return from reader
10325          */
10326         load : true,
10327         /**
10328          * @event loadexception
10329          * Fires if an exception occurs in the Proxy during loading.
10330          * Called with the signature of the Proxy's "loadexception" event.
10331          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10332          * 
10333          * @param {Proxy} 
10334          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10335          * @param {Object} load options 
10336          * @param {Object} jsonData from your request (normally this contains the Exception)
10337          */
10338         loadexception : true
10339     });
10340     
10341     if(this.proxy){
10342         this.proxy = Roo.factory(this.proxy, Roo.data);
10343         this.proxy.xmodule = this.xmodule || false;
10344         this.relayEvents(this.proxy,  ["loadexception"]);
10345     }
10346     this.sortToggle = {};
10347     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10348
10349     Roo.data.Store.superclass.constructor.call(this);
10350
10351     if(this.inlineData){
10352         this.loadData(this.inlineData);
10353         delete this.inlineData;
10354     }
10355 };
10356
10357 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10358      /**
10359     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10360     * without a remote query - used by combo/forms at present.
10361     */
10362     
10363     /**
10364     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10365     */
10366     /**
10367     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10368     */
10369     /**
10370     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10371     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10372     */
10373     /**
10374     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10375     * on any HTTP request
10376     */
10377     /**
10378     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10379     */
10380     /**
10381     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10382     */
10383     multiSort: false,
10384     /**
10385     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10386     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10387     */
10388     remoteSort : false,
10389
10390     /**
10391     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10392      * loaded or when a record is removed. (defaults to false).
10393     */
10394     pruneModifiedRecords : false,
10395
10396     // private
10397     lastOptions : null,
10398
10399     /**
10400      * Add Records to the Store and fires the add event.
10401      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10402      */
10403     add : function(records){
10404         records = [].concat(records);
10405         for(var i = 0, len = records.length; i < len; i++){
10406             records[i].join(this);
10407         }
10408         var index = this.data.length;
10409         this.data.addAll(records);
10410         this.fireEvent("add", this, records, index);
10411     },
10412
10413     /**
10414      * Remove a Record from the Store and fires the remove event.
10415      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10416      */
10417     remove : function(record){
10418         var index = this.data.indexOf(record);
10419         this.data.removeAt(index);
10420         if(this.pruneModifiedRecords){
10421             this.modified.remove(record);
10422         }
10423         this.fireEvent("remove", this, record, index);
10424     },
10425
10426     /**
10427      * Remove all Records from the Store and fires the clear event.
10428      */
10429     removeAll : function(){
10430         this.data.clear();
10431         if(this.pruneModifiedRecords){
10432             this.modified = [];
10433         }
10434         this.fireEvent("clear", this);
10435     },
10436
10437     /**
10438      * Inserts Records to the Store at the given index and fires the add event.
10439      * @param {Number} index The start index at which to insert the passed Records.
10440      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10441      */
10442     insert : function(index, records){
10443         records = [].concat(records);
10444         for(var i = 0, len = records.length; i < len; i++){
10445             this.data.insert(index, records[i]);
10446             records[i].join(this);
10447         }
10448         this.fireEvent("add", this, records, index);
10449     },
10450
10451     /**
10452      * Get the index within the cache of the passed Record.
10453      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10454      * @return {Number} The index of the passed Record. Returns -1 if not found.
10455      */
10456     indexOf : function(record){
10457         return this.data.indexOf(record);
10458     },
10459
10460     /**
10461      * Get the index within the cache of the Record with the passed id.
10462      * @param {String} id The id of the Record to find.
10463      * @return {Number} The index of the Record. Returns -1 if not found.
10464      */
10465     indexOfId : function(id){
10466         return this.data.indexOfKey(id);
10467     },
10468
10469     /**
10470      * Get the Record with the specified id.
10471      * @param {String} id The id of the Record to find.
10472      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10473      */
10474     getById : function(id){
10475         return this.data.key(id);
10476     },
10477
10478     /**
10479      * Get the Record at the specified index.
10480      * @param {Number} index The index of the Record to find.
10481      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10482      */
10483     getAt : function(index){
10484         return this.data.itemAt(index);
10485     },
10486
10487     /**
10488      * Returns a range of Records between specified indices.
10489      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10490      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10491      * @return {Roo.data.Record[]} An array of Records
10492      */
10493     getRange : function(start, end){
10494         return this.data.getRange(start, end);
10495     },
10496
10497     // private
10498     storeOptions : function(o){
10499         o = Roo.apply({}, o);
10500         delete o.callback;
10501         delete o.scope;
10502         this.lastOptions = o;
10503     },
10504
10505     /**
10506      * Loads the Record cache from the configured Proxy using the configured Reader.
10507      * <p>
10508      * If using remote paging, then the first load call must specify the <em>start</em>
10509      * and <em>limit</em> properties in the options.params property to establish the initial
10510      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10511      * <p>
10512      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10513      * and this call will return before the new data has been loaded. Perform any post-processing
10514      * in a callback function, or in a "load" event handler.</strong>
10515      * <p>
10516      * @param {Object} options An object containing properties which control loading options:<ul>
10517      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10518      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10519      * passed the following arguments:<ul>
10520      * <li>r : Roo.data.Record[]</li>
10521      * <li>options: Options object from the load call</li>
10522      * <li>success: Boolean success indicator</li></ul></li>
10523      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10524      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10525      * </ul>
10526      */
10527     load : function(options){
10528         options = options || {};
10529         if(this.fireEvent("beforeload", this, options) !== false){
10530             this.storeOptions(options);
10531             var p = Roo.apply(options.params || {}, this.baseParams);
10532             // if meta was not loaded from remote source.. try requesting it.
10533             if (!this.reader.metaFromRemote) {
10534                 p._requestMeta = 1;
10535             }
10536             if(this.sortInfo && this.remoteSort){
10537                 var pn = this.paramNames;
10538                 p[pn["sort"]] = this.sortInfo.field;
10539                 p[pn["dir"]] = this.sortInfo.direction;
10540             }
10541             if (this.multiSort) {
10542                 var pn = this.paramNames;
10543                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10544             }
10545             
10546             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10547         }
10548     },
10549
10550     /**
10551      * Reloads the Record cache from the configured Proxy using the configured Reader and
10552      * the options from the last load operation performed.
10553      * @param {Object} options (optional) An object containing properties which may override the options
10554      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10555      * the most recently used options are reused).
10556      */
10557     reload : function(options){
10558         this.load(Roo.applyIf(options||{}, this.lastOptions));
10559     },
10560
10561     // private
10562     // Called as a callback by the Reader during a load operation.
10563     loadRecords : function(o, options, success){
10564         if(!o || success === false){
10565             if(success !== false){
10566                 this.fireEvent("load", this, [], options, o);
10567             }
10568             if(options.callback){
10569                 options.callback.call(options.scope || this, [], options, false);
10570             }
10571             return;
10572         }
10573         // if data returned failure - throw an exception.
10574         if (o.success === false) {
10575             // show a message if no listener is registered.
10576             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10577                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10578             }
10579             // loadmask wil be hooked into this..
10580             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10581             return;
10582         }
10583         var r = o.records, t = o.totalRecords || r.length;
10584         
10585         this.fireEvent("beforeloadadd", this, r, options, o);
10586         
10587         if(!options || options.add !== true){
10588             if(this.pruneModifiedRecords){
10589                 this.modified = [];
10590             }
10591             for(var i = 0, len = r.length; i < len; i++){
10592                 r[i].join(this);
10593             }
10594             if(this.snapshot){
10595                 this.data = this.snapshot;
10596                 delete this.snapshot;
10597             }
10598             this.data.clear();
10599             this.data.addAll(r);
10600             this.totalLength = t;
10601             this.applySort();
10602             this.fireEvent("datachanged", this);
10603         }else{
10604             this.totalLength = Math.max(t, this.data.length+r.length);
10605             this.add(r);
10606         }
10607         this.fireEvent("load", this, r, options, o);
10608         if(options.callback){
10609             options.callback.call(options.scope || this, r, options, true);
10610         }
10611     },
10612
10613
10614     /**
10615      * Loads data from a passed data block. A Reader which understands the format of the data
10616      * must have been configured in the constructor.
10617      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10618      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10619      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10620      */
10621     loadData : function(o, append){
10622         var r = this.reader.readRecords(o);
10623         this.loadRecords(r, {add: append}, true);
10624     },
10625
10626     /**
10627      * Gets the number of cached records.
10628      * <p>
10629      * <em>If using paging, this may not be the total size of the dataset. If the data object
10630      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10631      * the data set size</em>
10632      */
10633     getCount : function(){
10634         return this.data.length || 0;
10635     },
10636
10637     /**
10638      * Gets the total number of records in the dataset as returned by the server.
10639      * <p>
10640      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10641      * the dataset size</em>
10642      */
10643     getTotalCount : function(){
10644         return this.totalLength || 0;
10645     },
10646
10647     /**
10648      * Returns the sort state of the Store as an object with two properties:
10649      * <pre><code>
10650  field {String} The name of the field by which the Records are sorted
10651  direction {String} The sort order, "ASC" or "DESC"
10652      * </code></pre>
10653      */
10654     getSortState : function(){
10655         return this.sortInfo;
10656     },
10657
10658     // private
10659     applySort : function(){
10660         if(this.sortInfo && !this.remoteSort){
10661             var s = this.sortInfo, f = s.field;
10662             var st = this.fields.get(f).sortType;
10663             var fn = function(r1, r2){
10664                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10665                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10666             };
10667             this.data.sort(s.direction, fn);
10668             if(this.snapshot && this.snapshot != this.data){
10669                 this.snapshot.sort(s.direction, fn);
10670             }
10671         }
10672     },
10673
10674     /**
10675      * Sets the default sort column and order to be used by the next load operation.
10676      * @param {String} fieldName The name of the field to sort by.
10677      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10678      */
10679     setDefaultSort : function(field, dir){
10680         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10681     },
10682
10683     /**
10684      * Sort the Records.
10685      * If remote sorting is used, the sort is performed on the server, and the cache is
10686      * reloaded. If local sorting is used, the cache is sorted internally.
10687      * @param {String} fieldName The name of the field to sort by.
10688      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10689      */
10690     sort : function(fieldName, dir){
10691         var f = this.fields.get(fieldName);
10692         if(!dir){
10693             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10694             
10695             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10696                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10697             }else{
10698                 dir = f.sortDir;
10699             }
10700         }
10701         this.sortToggle[f.name] = dir;
10702         this.sortInfo = {field: f.name, direction: dir};
10703         if(!this.remoteSort){
10704             this.applySort();
10705             this.fireEvent("datachanged", this);
10706         }else{
10707             this.load(this.lastOptions);
10708         }
10709     },
10710
10711     /**
10712      * Calls the specified function for each of the Records in the cache.
10713      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10714      * Returning <em>false</em> aborts and exits the iteration.
10715      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10716      */
10717     each : function(fn, scope){
10718         this.data.each(fn, scope);
10719     },
10720
10721     /**
10722      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10723      * (e.g., during paging).
10724      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10725      */
10726     getModifiedRecords : function(){
10727         return this.modified;
10728     },
10729
10730     // private
10731     createFilterFn : function(property, value, anyMatch){
10732         if(!value.exec){ // not a regex
10733             value = String(value);
10734             if(value.length == 0){
10735                 return false;
10736             }
10737             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10738         }
10739         return function(r){
10740             return value.test(r.data[property]);
10741         };
10742     },
10743
10744     /**
10745      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10746      * @param {String} property A field on your records
10747      * @param {Number} start The record index to start at (defaults to 0)
10748      * @param {Number} end The last record index to include (defaults to length - 1)
10749      * @return {Number} The sum
10750      */
10751     sum : function(property, start, end){
10752         var rs = this.data.items, v = 0;
10753         start = start || 0;
10754         end = (end || end === 0) ? end : rs.length-1;
10755
10756         for(var i = start; i <= end; i++){
10757             v += (rs[i].data[property] || 0);
10758         }
10759         return v;
10760     },
10761
10762     /**
10763      * Filter the records by a specified property.
10764      * @param {String} field A field on your records
10765      * @param {String/RegExp} value Either a string that the field
10766      * should start with or a RegExp to test against the field
10767      * @param {Boolean} anyMatch True to match any part not just the beginning
10768      */
10769     filter : function(property, value, anyMatch){
10770         var fn = this.createFilterFn(property, value, anyMatch);
10771         return fn ? this.filterBy(fn) : this.clearFilter();
10772     },
10773
10774     /**
10775      * Filter by a function. The specified function will be called with each
10776      * record in this data source. If the function returns true the record is included,
10777      * otherwise it is filtered.
10778      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10779      * @param {Object} scope (optional) The scope of the function (defaults to this)
10780      */
10781     filterBy : function(fn, scope){
10782         this.snapshot = this.snapshot || this.data;
10783         this.data = this.queryBy(fn, scope||this);
10784         this.fireEvent("datachanged", this);
10785     },
10786
10787     /**
10788      * Query the records by a specified property.
10789      * @param {String} field A field on your records
10790      * @param {String/RegExp} value Either a string that the field
10791      * should start with or a RegExp to test against the field
10792      * @param {Boolean} anyMatch True to match any part not just the beginning
10793      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10794      */
10795     query : function(property, value, anyMatch){
10796         var fn = this.createFilterFn(property, value, anyMatch);
10797         return fn ? this.queryBy(fn) : this.data.clone();
10798     },
10799
10800     /**
10801      * Query by a function. The specified function will be called with each
10802      * record in this data source. If the function returns true the record is included
10803      * in the results.
10804      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10805      * @param {Object} scope (optional) The scope of the function (defaults to this)
10806       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10807      **/
10808     queryBy : function(fn, scope){
10809         var data = this.snapshot || this.data;
10810         return data.filterBy(fn, scope||this);
10811     },
10812
10813     /**
10814      * Collects unique values for a particular dataIndex from this store.
10815      * @param {String} dataIndex The property to collect
10816      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10817      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10818      * @return {Array} An array of the unique values
10819      **/
10820     collect : function(dataIndex, allowNull, bypassFilter){
10821         var d = (bypassFilter === true && this.snapshot) ?
10822                 this.snapshot.items : this.data.items;
10823         var v, sv, r = [], l = {};
10824         for(var i = 0, len = d.length; i < len; i++){
10825             v = d[i].data[dataIndex];
10826             sv = String(v);
10827             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10828                 l[sv] = true;
10829                 r[r.length] = v;
10830             }
10831         }
10832         return r;
10833     },
10834
10835     /**
10836      * Revert to a view of the Record cache with no filtering applied.
10837      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10838      */
10839     clearFilter : function(suppressEvent){
10840         if(this.snapshot && this.snapshot != this.data){
10841             this.data = this.snapshot;
10842             delete this.snapshot;
10843             if(suppressEvent !== true){
10844                 this.fireEvent("datachanged", this);
10845             }
10846         }
10847     },
10848
10849     // private
10850     afterEdit : function(record){
10851         if(this.modified.indexOf(record) == -1){
10852             this.modified.push(record);
10853         }
10854         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10855     },
10856     
10857     // private
10858     afterReject : function(record){
10859         this.modified.remove(record);
10860         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10861     },
10862
10863     // private
10864     afterCommit : function(record){
10865         this.modified.remove(record);
10866         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10867     },
10868
10869     /**
10870      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10871      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10872      */
10873     commitChanges : function(){
10874         var m = this.modified.slice(0);
10875         this.modified = [];
10876         for(var i = 0, len = m.length; i < len; i++){
10877             m[i].commit();
10878         }
10879     },
10880
10881     /**
10882      * Cancel outstanding changes on all changed records.
10883      */
10884     rejectChanges : function(){
10885         var m = this.modified.slice(0);
10886         this.modified = [];
10887         for(var i = 0, len = m.length; i < len; i++){
10888             m[i].reject();
10889         }
10890     },
10891
10892     onMetaChange : function(meta, rtype, o){
10893         this.recordType = rtype;
10894         this.fields = rtype.prototype.fields;
10895         delete this.snapshot;
10896         this.sortInfo = meta.sortInfo || this.sortInfo;
10897         this.modified = [];
10898         this.fireEvent('metachange', this, this.reader.meta);
10899     },
10900     
10901     moveIndex : function(data, type)
10902     {
10903         var index = this.indexOf(data);
10904         
10905         var newIndex = index + type;
10906         
10907         this.remove(data);
10908         
10909         this.insert(newIndex, data);
10910         
10911     }
10912 });/*
10913  * Based on:
10914  * Ext JS Library 1.1.1
10915  * Copyright(c) 2006-2007, Ext JS, LLC.
10916  *
10917  * Originally Released Under LGPL - original licence link has changed is not relivant.
10918  *
10919  * Fork - LGPL
10920  * <script type="text/javascript">
10921  */
10922
10923 /**
10924  * @class Roo.data.SimpleStore
10925  * @extends Roo.data.Store
10926  * Small helper class to make creating Stores from Array data easier.
10927  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10928  * @cfg {Array} fields An array of field definition objects, or field name strings.
10929  * @cfg {Array} data The multi-dimensional array of data
10930  * @constructor
10931  * @param {Object} config
10932  */
10933 Roo.data.SimpleStore = function(config){
10934     Roo.data.SimpleStore.superclass.constructor.call(this, {
10935         isLocal : true,
10936         reader: new Roo.data.ArrayReader({
10937                 id: config.id
10938             },
10939             Roo.data.Record.create(config.fields)
10940         ),
10941         proxy : new Roo.data.MemoryProxy(config.data)
10942     });
10943     this.load();
10944 };
10945 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10946  * Based on:
10947  * Ext JS Library 1.1.1
10948  * Copyright(c) 2006-2007, Ext JS, LLC.
10949  *
10950  * Originally Released Under LGPL - original licence link has changed is not relivant.
10951  *
10952  * Fork - LGPL
10953  * <script type="text/javascript">
10954  */
10955
10956 /**
10957 /**
10958  * @extends Roo.data.Store
10959  * @class Roo.data.JsonStore
10960  * Small helper class to make creating Stores for JSON data easier. <br/>
10961 <pre><code>
10962 var store = new Roo.data.JsonStore({
10963     url: 'get-images.php',
10964     root: 'images',
10965     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10966 });
10967 </code></pre>
10968  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10969  * JsonReader and HttpProxy (unless inline data is provided).</b>
10970  * @cfg {Array} fields An array of field definition objects, or field name strings.
10971  * @constructor
10972  * @param {Object} config
10973  */
10974 Roo.data.JsonStore = function(c){
10975     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10976         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10977         reader: new Roo.data.JsonReader(c, c.fields)
10978     }));
10979 };
10980 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10981  * Based on:
10982  * Ext JS Library 1.1.1
10983  * Copyright(c) 2006-2007, Ext JS, LLC.
10984  *
10985  * Originally Released Under LGPL - original licence link has changed is not relivant.
10986  *
10987  * Fork - LGPL
10988  * <script type="text/javascript">
10989  */
10990
10991  
10992 Roo.data.Field = function(config){
10993     if(typeof config == "string"){
10994         config = {name: config};
10995     }
10996     Roo.apply(this, config);
10997     
10998     if(!this.type){
10999         this.type = "auto";
11000     }
11001     
11002     var st = Roo.data.SortTypes;
11003     // named sortTypes are supported, here we look them up
11004     if(typeof this.sortType == "string"){
11005         this.sortType = st[this.sortType];
11006     }
11007     
11008     // set default sortType for strings and dates
11009     if(!this.sortType){
11010         switch(this.type){
11011             case "string":
11012                 this.sortType = st.asUCString;
11013                 break;
11014             case "date":
11015                 this.sortType = st.asDate;
11016                 break;
11017             default:
11018                 this.sortType = st.none;
11019         }
11020     }
11021
11022     // define once
11023     var stripRe = /[\$,%]/g;
11024
11025     // prebuilt conversion function for this field, instead of
11026     // switching every time we're reading a value
11027     if(!this.convert){
11028         var cv, dateFormat = this.dateFormat;
11029         switch(this.type){
11030             case "":
11031             case "auto":
11032             case undefined:
11033                 cv = function(v){ return v; };
11034                 break;
11035             case "string":
11036                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11037                 break;
11038             case "int":
11039                 cv = function(v){
11040                     return v !== undefined && v !== null && v !== '' ?
11041                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11042                     };
11043                 break;
11044             case "float":
11045                 cv = function(v){
11046                     return v !== undefined && v !== null && v !== '' ?
11047                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11048                     };
11049                 break;
11050             case "bool":
11051             case "boolean":
11052                 cv = function(v){ return v === true || v === "true" || v == 1; };
11053                 break;
11054             case "date":
11055                 cv = function(v){
11056                     if(!v){
11057                         return '';
11058                     }
11059                     if(v instanceof Date){
11060                         return v;
11061                     }
11062                     if(dateFormat){
11063                         if(dateFormat == "timestamp"){
11064                             return new Date(v*1000);
11065                         }
11066                         return Date.parseDate(v, dateFormat);
11067                     }
11068                     var parsed = Date.parse(v);
11069                     return parsed ? new Date(parsed) : null;
11070                 };
11071              break;
11072             
11073         }
11074         this.convert = cv;
11075     }
11076 };
11077
11078 Roo.data.Field.prototype = {
11079     dateFormat: null,
11080     defaultValue: "",
11081     mapping: null,
11082     sortType : null,
11083     sortDir : "ASC"
11084 };/*
11085  * Based on:
11086  * Ext JS Library 1.1.1
11087  * Copyright(c) 2006-2007, Ext JS, LLC.
11088  *
11089  * Originally Released Under LGPL - original licence link has changed is not relivant.
11090  *
11091  * Fork - LGPL
11092  * <script type="text/javascript">
11093  */
11094  
11095 // Base class for reading structured data from a data source.  This class is intended to be
11096 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11097
11098 /**
11099  * @class Roo.data.DataReader
11100  * Base class for reading structured data from a data source.  This class is intended to be
11101  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11102  */
11103
11104 Roo.data.DataReader = function(meta, recordType){
11105     
11106     this.meta = meta;
11107     
11108     this.recordType = recordType instanceof Array ? 
11109         Roo.data.Record.create(recordType) : recordType;
11110 };
11111
11112 Roo.data.DataReader.prototype = {
11113      /**
11114      * Create an empty record
11115      * @param {Object} data (optional) - overlay some values
11116      * @return {Roo.data.Record} record created.
11117      */
11118     newRow :  function(d) {
11119         var da =  {};
11120         this.recordType.prototype.fields.each(function(c) {
11121             switch( c.type) {
11122                 case 'int' : da[c.name] = 0; break;
11123                 case 'date' : da[c.name] = new Date(); break;
11124                 case 'float' : da[c.name] = 0.0; break;
11125                 case 'boolean' : da[c.name] = false; break;
11126                 default : da[c.name] = ""; break;
11127             }
11128             
11129         });
11130         return new this.recordType(Roo.apply(da, d));
11131     }
11132     
11133 };/*
11134  * Based on:
11135  * Ext JS Library 1.1.1
11136  * Copyright(c) 2006-2007, Ext JS, LLC.
11137  *
11138  * Originally Released Under LGPL - original licence link has changed is not relivant.
11139  *
11140  * Fork - LGPL
11141  * <script type="text/javascript">
11142  */
11143
11144 /**
11145  * @class Roo.data.DataProxy
11146  * @extends Roo.data.Observable
11147  * This class is an abstract base class for implementations which provide retrieval of
11148  * unformatted data objects.<br>
11149  * <p>
11150  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11151  * (of the appropriate type which knows how to parse the data object) to provide a block of
11152  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11153  * <p>
11154  * Custom implementations must implement the load method as described in
11155  * {@link Roo.data.HttpProxy#load}.
11156  */
11157 Roo.data.DataProxy = function(){
11158     this.addEvents({
11159         /**
11160          * @event beforeload
11161          * Fires before a network request is made to retrieve a data object.
11162          * @param {Object} This DataProxy object.
11163          * @param {Object} params The params parameter to the load function.
11164          */
11165         beforeload : true,
11166         /**
11167          * @event load
11168          * Fires before the load method's callback is called.
11169          * @param {Object} This DataProxy object.
11170          * @param {Object} o The data object.
11171          * @param {Object} arg The callback argument object passed to the load function.
11172          */
11173         load : true,
11174         /**
11175          * @event loadexception
11176          * Fires if an Exception occurs during data retrieval.
11177          * @param {Object} This DataProxy object.
11178          * @param {Object} o The data object.
11179          * @param {Object} arg The callback argument object passed to the load function.
11180          * @param {Object} e The Exception.
11181          */
11182         loadexception : true
11183     });
11184     Roo.data.DataProxy.superclass.constructor.call(this);
11185 };
11186
11187 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11188
11189     /**
11190      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11191      */
11192 /*
11193  * Based on:
11194  * Ext JS Library 1.1.1
11195  * Copyright(c) 2006-2007, Ext JS, LLC.
11196  *
11197  * Originally Released Under LGPL - original licence link has changed is not relivant.
11198  *
11199  * Fork - LGPL
11200  * <script type="text/javascript">
11201  */
11202 /**
11203  * @class Roo.data.MemoryProxy
11204  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11205  * to the Reader when its load method is called.
11206  * @constructor
11207  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11208  */
11209 Roo.data.MemoryProxy = function(data){
11210     if (data.data) {
11211         data = data.data;
11212     }
11213     Roo.data.MemoryProxy.superclass.constructor.call(this);
11214     this.data = data;
11215 };
11216
11217 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11218     
11219     /**
11220      * Load data from the requested source (in this case an in-memory
11221      * data object passed to the constructor), read the data object into
11222      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11223      * process that block using the passed callback.
11224      * @param {Object} params This parameter is not used by the MemoryProxy class.
11225      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11226      * object into a block of Roo.data.Records.
11227      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11228      * The function must be passed <ul>
11229      * <li>The Record block object</li>
11230      * <li>The "arg" argument from the load function</li>
11231      * <li>A boolean success indicator</li>
11232      * </ul>
11233      * @param {Object} scope The scope in which to call the callback
11234      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11235      */
11236     load : function(params, reader, callback, scope, arg){
11237         params = params || {};
11238         var result;
11239         try {
11240             result = reader.readRecords(this.data);
11241         }catch(e){
11242             this.fireEvent("loadexception", this, arg, null, e);
11243             callback.call(scope, null, arg, false);
11244             return;
11245         }
11246         callback.call(scope, result, arg, true);
11247     },
11248     
11249     // private
11250     update : function(params, records){
11251         
11252     }
11253 });/*
11254  * Based on:
11255  * Ext JS Library 1.1.1
11256  * Copyright(c) 2006-2007, Ext JS, LLC.
11257  *
11258  * Originally Released Under LGPL - original licence link has changed is not relivant.
11259  *
11260  * Fork - LGPL
11261  * <script type="text/javascript">
11262  */
11263 /**
11264  * @class Roo.data.HttpProxy
11265  * @extends Roo.data.DataProxy
11266  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11267  * configured to reference a certain URL.<br><br>
11268  * <p>
11269  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11270  * from which the running page was served.<br><br>
11271  * <p>
11272  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11273  * <p>
11274  * Be aware that to enable the browser to parse an XML document, the server must set
11275  * the Content-Type header in the HTTP response to "text/xml".
11276  * @constructor
11277  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11278  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11279  * will be used to make the request.
11280  */
11281 Roo.data.HttpProxy = function(conn){
11282     Roo.data.HttpProxy.superclass.constructor.call(this);
11283     // is conn a conn config or a real conn?
11284     this.conn = conn;
11285     this.useAjax = !conn || !conn.events;
11286   
11287 };
11288
11289 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11290     // thse are take from connection...
11291     
11292     /**
11293      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11294      */
11295     /**
11296      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11297      * extra parameters to each request made by this object. (defaults to undefined)
11298      */
11299     /**
11300      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11301      *  to each request made by this object. (defaults to undefined)
11302      */
11303     /**
11304      * @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)
11305      */
11306     /**
11307      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11308      */
11309      /**
11310      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11311      * @type Boolean
11312      */
11313   
11314
11315     /**
11316      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11317      * @type Boolean
11318      */
11319     /**
11320      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11321      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11322      * a finer-grained basis than the DataProxy events.
11323      */
11324     getConnection : function(){
11325         return this.useAjax ? Roo.Ajax : this.conn;
11326     },
11327
11328     /**
11329      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11330      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11331      * process that block using the passed callback.
11332      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11333      * for the request to the remote server.
11334      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11335      * object into a block of Roo.data.Records.
11336      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11337      * The function must be passed <ul>
11338      * <li>The Record block object</li>
11339      * <li>The "arg" argument from the load function</li>
11340      * <li>A boolean success indicator</li>
11341      * </ul>
11342      * @param {Object} scope The scope in which to call the callback
11343      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11344      */
11345     load : function(params, reader, callback, scope, arg){
11346         if(this.fireEvent("beforeload", this, params) !== false){
11347             var  o = {
11348                 params : params || {},
11349                 request: {
11350                     callback : callback,
11351                     scope : scope,
11352                     arg : arg
11353                 },
11354                 reader: reader,
11355                 callback : this.loadResponse,
11356                 scope: this
11357             };
11358             if(this.useAjax){
11359                 Roo.applyIf(o, this.conn);
11360                 if(this.activeRequest){
11361                     Roo.Ajax.abort(this.activeRequest);
11362                 }
11363                 this.activeRequest = Roo.Ajax.request(o);
11364             }else{
11365                 this.conn.request(o);
11366             }
11367         }else{
11368             callback.call(scope||this, null, arg, false);
11369         }
11370     },
11371
11372     // private
11373     loadResponse : function(o, success, response){
11374         delete this.activeRequest;
11375         if(!success){
11376             this.fireEvent("loadexception", this, o, response);
11377             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11378             return;
11379         }
11380         var result;
11381         try {
11382             result = o.reader.read(response);
11383         }catch(e){
11384             this.fireEvent("loadexception", this, o, response, e);
11385             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11386             return;
11387         }
11388         
11389         this.fireEvent("load", this, o, o.request.arg);
11390         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11391     },
11392
11393     // private
11394     update : function(dataSet){
11395
11396     },
11397
11398     // private
11399     updateResponse : function(dataSet){
11400
11401     }
11402 });/*
11403  * Based on:
11404  * Ext JS Library 1.1.1
11405  * Copyright(c) 2006-2007, Ext JS, LLC.
11406  *
11407  * Originally Released Under LGPL - original licence link has changed is not relivant.
11408  *
11409  * Fork - LGPL
11410  * <script type="text/javascript">
11411  */
11412
11413 /**
11414  * @class Roo.data.ScriptTagProxy
11415  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11416  * other than the originating domain of the running page.<br><br>
11417  * <p>
11418  * <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
11419  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11420  * <p>
11421  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11422  * source code that is used as the source inside a &lt;script> tag.<br><br>
11423  * <p>
11424  * In order for the browser to process the returned data, the server must wrap the data object
11425  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11426  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11427  * depending on whether the callback name was passed:
11428  * <p>
11429  * <pre><code>
11430 boolean scriptTag = false;
11431 String cb = request.getParameter("callback");
11432 if (cb != null) {
11433     scriptTag = true;
11434     response.setContentType("text/javascript");
11435 } else {
11436     response.setContentType("application/x-json");
11437 }
11438 Writer out = response.getWriter();
11439 if (scriptTag) {
11440     out.write(cb + "(");
11441 }
11442 out.print(dataBlock.toJsonString());
11443 if (scriptTag) {
11444     out.write(");");
11445 }
11446 </pre></code>
11447  *
11448  * @constructor
11449  * @param {Object} config A configuration object.
11450  */
11451 Roo.data.ScriptTagProxy = function(config){
11452     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11453     Roo.apply(this, config);
11454     this.head = document.getElementsByTagName("head")[0];
11455 };
11456
11457 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11458
11459 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11460     /**
11461      * @cfg {String} url The URL from which to request the data object.
11462      */
11463     /**
11464      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11465      */
11466     timeout : 30000,
11467     /**
11468      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11469      * the server the name of the callback function set up by the load call to process the returned data object.
11470      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11471      * javascript output which calls this named function passing the data object as its only parameter.
11472      */
11473     callbackParam : "callback",
11474     /**
11475      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11476      * name to the request.
11477      */
11478     nocache : true,
11479
11480     /**
11481      * Load data from the configured URL, read the data object into
11482      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11483      * process that block using the passed callback.
11484      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11485      * for the request to the remote server.
11486      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11487      * object into a block of Roo.data.Records.
11488      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11489      * The function must be passed <ul>
11490      * <li>The Record block object</li>
11491      * <li>The "arg" argument from the load function</li>
11492      * <li>A boolean success indicator</li>
11493      * </ul>
11494      * @param {Object} scope The scope in which to call the callback
11495      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11496      */
11497     load : function(params, reader, callback, scope, arg){
11498         if(this.fireEvent("beforeload", this, params) !== false){
11499
11500             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11501
11502             var url = this.url;
11503             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11504             if(this.nocache){
11505                 url += "&_dc=" + (new Date().getTime());
11506             }
11507             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11508             var trans = {
11509                 id : transId,
11510                 cb : "stcCallback"+transId,
11511                 scriptId : "stcScript"+transId,
11512                 params : params,
11513                 arg : arg,
11514                 url : url,
11515                 callback : callback,
11516                 scope : scope,
11517                 reader : reader
11518             };
11519             var conn = this;
11520
11521             window[trans.cb] = function(o){
11522                 conn.handleResponse(o, trans);
11523             };
11524
11525             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11526
11527             if(this.autoAbort !== false){
11528                 this.abort();
11529             }
11530
11531             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11532
11533             var script = document.createElement("script");
11534             script.setAttribute("src", url);
11535             script.setAttribute("type", "text/javascript");
11536             script.setAttribute("id", trans.scriptId);
11537             this.head.appendChild(script);
11538
11539             this.trans = trans;
11540         }else{
11541             callback.call(scope||this, null, arg, false);
11542         }
11543     },
11544
11545     // private
11546     isLoading : function(){
11547         return this.trans ? true : false;
11548     },
11549
11550     /**
11551      * Abort the current server request.
11552      */
11553     abort : function(){
11554         if(this.isLoading()){
11555             this.destroyTrans(this.trans);
11556         }
11557     },
11558
11559     // private
11560     destroyTrans : function(trans, isLoaded){
11561         this.head.removeChild(document.getElementById(trans.scriptId));
11562         clearTimeout(trans.timeoutId);
11563         if(isLoaded){
11564             window[trans.cb] = undefined;
11565             try{
11566                 delete window[trans.cb];
11567             }catch(e){}
11568         }else{
11569             // if hasn't been loaded, wait for load to remove it to prevent script error
11570             window[trans.cb] = function(){
11571                 window[trans.cb] = undefined;
11572                 try{
11573                     delete window[trans.cb];
11574                 }catch(e){}
11575             };
11576         }
11577     },
11578
11579     // private
11580     handleResponse : function(o, trans){
11581         this.trans = false;
11582         this.destroyTrans(trans, true);
11583         var result;
11584         try {
11585             result = trans.reader.readRecords(o);
11586         }catch(e){
11587             this.fireEvent("loadexception", this, o, trans.arg, e);
11588             trans.callback.call(trans.scope||window, null, trans.arg, false);
11589             return;
11590         }
11591         this.fireEvent("load", this, o, trans.arg);
11592         trans.callback.call(trans.scope||window, result, trans.arg, true);
11593     },
11594
11595     // private
11596     handleFailure : function(trans){
11597         this.trans = false;
11598         this.destroyTrans(trans, false);
11599         this.fireEvent("loadexception", this, null, trans.arg);
11600         trans.callback.call(trans.scope||window, null, trans.arg, false);
11601     }
11602 });/*
11603  * Based on:
11604  * Ext JS Library 1.1.1
11605  * Copyright(c) 2006-2007, Ext JS, LLC.
11606  *
11607  * Originally Released Under LGPL - original licence link has changed is not relivant.
11608  *
11609  * Fork - LGPL
11610  * <script type="text/javascript">
11611  */
11612
11613 /**
11614  * @class Roo.data.JsonReader
11615  * @extends Roo.data.DataReader
11616  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11617  * based on mappings in a provided Roo.data.Record constructor.
11618  * 
11619  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11620  * in the reply previously. 
11621  * 
11622  * <p>
11623  * Example code:
11624  * <pre><code>
11625 var RecordDef = Roo.data.Record.create([
11626     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11627     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11628 ]);
11629 var myReader = new Roo.data.JsonReader({
11630     totalProperty: "results",    // The property which contains the total dataset size (optional)
11631     root: "rows",                // The property which contains an Array of row objects
11632     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11633 }, RecordDef);
11634 </code></pre>
11635  * <p>
11636  * This would consume a JSON file like this:
11637  * <pre><code>
11638 { 'results': 2, 'rows': [
11639     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11640     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11641 }
11642 </code></pre>
11643  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11644  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11645  * paged from the remote server.
11646  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11647  * @cfg {String} root name of the property which contains the Array of row objects.
11648  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11649  * @cfg {Array} fields Array of field definition objects
11650  * @constructor
11651  * Create a new JsonReader
11652  * @param {Object} meta Metadata configuration options
11653  * @param {Object} recordType Either an Array of field definition objects,
11654  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11655  */
11656 Roo.data.JsonReader = function(meta, recordType){
11657     
11658     meta = meta || {};
11659     // set some defaults:
11660     Roo.applyIf(meta, {
11661         totalProperty: 'total',
11662         successProperty : 'success',
11663         root : 'data',
11664         id : 'id'
11665     });
11666     
11667     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11668 };
11669 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11670     
11671     /**
11672      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11673      * Used by Store query builder to append _requestMeta to params.
11674      * 
11675      */
11676     metaFromRemote : false,
11677     /**
11678      * This method is only used by a DataProxy which has retrieved data from a remote server.
11679      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11680      * @return {Object} data A data block which is used by an Roo.data.Store object as
11681      * a cache of Roo.data.Records.
11682      */
11683     read : function(response){
11684         var json = response.responseText;
11685        
11686         var o = /* eval:var:o */ eval("("+json+")");
11687         if(!o) {
11688             throw {message: "JsonReader.read: Json object not found"};
11689         }
11690         
11691         if(o.metaData){
11692             
11693             delete this.ef;
11694             this.metaFromRemote = true;
11695             this.meta = o.metaData;
11696             this.recordType = Roo.data.Record.create(o.metaData.fields);
11697             this.onMetaChange(this.meta, this.recordType, o);
11698         }
11699         return this.readRecords(o);
11700     },
11701
11702     // private function a store will implement
11703     onMetaChange : function(meta, recordType, o){
11704
11705     },
11706
11707     /**
11708          * @ignore
11709          */
11710     simpleAccess: function(obj, subsc) {
11711         return obj[subsc];
11712     },
11713
11714         /**
11715          * @ignore
11716          */
11717     getJsonAccessor: function(){
11718         var re = /[\[\.]/;
11719         return function(expr) {
11720             try {
11721                 return(re.test(expr))
11722                     ? new Function("obj", "return obj." + expr)
11723                     : function(obj){
11724                         return obj[expr];
11725                     };
11726             } catch(e){}
11727             return Roo.emptyFn;
11728         };
11729     }(),
11730
11731     /**
11732      * Create a data block containing Roo.data.Records from an XML document.
11733      * @param {Object} o An object which contains an Array of row objects in the property specified
11734      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11735      * which contains the total size of the dataset.
11736      * @return {Object} data A data block which is used by an Roo.data.Store object as
11737      * a cache of Roo.data.Records.
11738      */
11739     readRecords : function(o){
11740         /**
11741          * After any data loads, the raw JSON data is available for further custom processing.
11742          * @type Object
11743          */
11744         this.o = o;
11745         var s = this.meta, Record = this.recordType,
11746             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11747
11748 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11749         if (!this.ef) {
11750             if(s.totalProperty) {
11751                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11752                 }
11753                 if(s.successProperty) {
11754                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11755                 }
11756                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11757                 if (s.id) {
11758                         var g = this.getJsonAccessor(s.id);
11759                         this.getId = function(rec) {
11760                                 var r = g(rec);  
11761                                 return (r === undefined || r === "") ? null : r;
11762                         };
11763                 } else {
11764                         this.getId = function(){return null;};
11765                 }
11766             this.ef = [];
11767             for(var jj = 0; jj < fl; jj++){
11768                 f = fi[jj];
11769                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11770                 this.ef[jj] = this.getJsonAccessor(map);
11771             }
11772         }
11773
11774         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11775         if(s.totalProperty){
11776             var vt = parseInt(this.getTotal(o), 10);
11777             if(!isNaN(vt)){
11778                 totalRecords = vt;
11779             }
11780         }
11781         if(s.successProperty){
11782             var vs = this.getSuccess(o);
11783             if(vs === false || vs === 'false'){
11784                 success = false;
11785             }
11786         }
11787         var records = [];
11788         for(var i = 0; i < c; i++){
11789                 var n = root[i];
11790             var values = {};
11791             var id = this.getId(n);
11792             for(var j = 0; j < fl; j++){
11793                 f = fi[j];
11794             var v = this.ef[j](n);
11795             if (!f.convert) {
11796                 Roo.log('missing convert for ' + f.name);
11797                 Roo.log(f);
11798                 continue;
11799             }
11800             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11801             }
11802             var record = new Record(values, id);
11803             record.json = n;
11804             records[i] = record;
11805         }
11806         return {
11807             raw : o,
11808             success : success,
11809             records : records,
11810             totalRecords : totalRecords
11811         };
11812     }
11813 });/*
11814  * Based on:
11815  * Ext JS Library 1.1.1
11816  * Copyright(c) 2006-2007, Ext JS, LLC.
11817  *
11818  * Originally Released Under LGPL - original licence link has changed is not relivant.
11819  *
11820  * Fork - LGPL
11821  * <script type="text/javascript">
11822  */
11823
11824 /**
11825  * @class Roo.data.ArrayReader
11826  * @extends Roo.data.DataReader
11827  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11828  * Each element of that Array represents a row of data fields. The
11829  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11830  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11831  * <p>
11832  * Example code:.
11833  * <pre><code>
11834 var RecordDef = Roo.data.Record.create([
11835     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11836     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11837 ]);
11838 var myReader = new Roo.data.ArrayReader({
11839     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11840 }, RecordDef);
11841 </code></pre>
11842  * <p>
11843  * This would consume an Array like this:
11844  * <pre><code>
11845 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11846   </code></pre>
11847  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11848  * @constructor
11849  * Create a new JsonReader
11850  * @param {Object} meta Metadata configuration options.
11851  * @param {Object} recordType Either an Array of field definition objects
11852  * as specified to {@link Roo.data.Record#create},
11853  * or an {@link Roo.data.Record} object
11854  * created using {@link Roo.data.Record#create}.
11855  */
11856 Roo.data.ArrayReader = function(meta, recordType){
11857     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11858 };
11859
11860 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11861     /**
11862      * Create a data block containing Roo.data.Records from an XML document.
11863      * @param {Object} o An Array of row objects which represents the dataset.
11864      * @return {Object} data A data block which is used by an Roo.data.Store object as
11865      * a cache of Roo.data.Records.
11866      */
11867     readRecords : function(o){
11868         var sid = this.meta ? this.meta.id : null;
11869         var recordType = this.recordType, fields = recordType.prototype.fields;
11870         var records = [];
11871         var root = o;
11872             for(var i = 0; i < root.length; i++){
11873                     var n = root[i];
11874                 var values = {};
11875                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11876                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11877                 var f = fields.items[j];
11878                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11879                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11880                 v = f.convert(v);
11881                 values[f.name] = v;
11882             }
11883                 var record = new recordType(values, id);
11884                 record.json = n;
11885                 records[records.length] = record;
11886             }
11887             return {
11888                 records : records,
11889                 totalRecords : records.length
11890             };
11891     }
11892 });/*
11893  * - LGPL
11894  * * 
11895  */
11896
11897 /**
11898  * @class Roo.bootstrap.ComboBox
11899  * @extends Roo.bootstrap.TriggerField
11900  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11901  * @cfg {Boolean} append (true|false) default false
11902  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11903  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11904  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11905  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11906  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11907  * @cfg {Boolean} animate default true
11908  * @cfg {Boolean} emptyResultText only for touch device
11909  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11910  * @constructor
11911  * Create a new ComboBox.
11912  * @param {Object} config Configuration options
11913  */
11914 Roo.bootstrap.ComboBox = function(config){
11915     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11916     this.addEvents({
11917         /**
11918          * @event expand
11919          * Fires when the dropdown list is expanded
11920              * @param {Roo.bootstrap.ComboBox} combo This combo box
11921              */
11922         'expand' : true,
11923         /**
11924          * @event collapse
11925          * Fires when the dropdown list is collapsed
11926              * @param {Roo.bootstrap.ComboBox} combo This combo box
11927              */
11928         'collapse' : true,
11929         /**
11930          * @event beforeselect
11931          * Fires before a list item is selected. Return false to cancel the selection.
11932              * @param {Roo.bootstrap.ComboBox} combo This combo box
11933              * @param {Roo.data.Record} record The data record returned from the underlying store
11934              * @param {Number} index The index of the selected item in the dropdown list
11935              */
11936         'beforeselect' : true,
11937         /**
11938          * @event select
11939          * Fires when a list item is selected
11940              * @param {Roo.bootstrap.ComboBox} combo This combo box
11941              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11942              * @param {Number} index The index of the selected item in the dropdown list
11943              */
11944         'select' : true,
11945         /**
11946          * @event beforequery
11947          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11948          * The event object passed has these properties:
11949              * @param {Roo.bootstrap.ComboBox} combo This combo box
11950              * @param {String} query The query
11951              * @param {Boolean} forceAll true to force "all" query
11952              * @param {Boolean} cancel true to cancel the query
11953              * @param {Object} e The query event object
11954              */
11955         'beforequery': true,
11956          /**
11957          * @event add
11958          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11959              * @param {Roo.bootstrap.ComboBox} combo This combo box
11960              */
11961         'add' : true,
11962         /**
11963          * @event edit
11964          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11965              * @param {Roo.bootstrap.ComboBox} combo This combo box
11966              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11967              */
11968         'edit' : true,
11969         /**
11970          * @event remove
11971          * Fires when the remove value from the combobox array
11972              * @param {Roo.bootstrap.ComboBox} combo This combo box
11973              */
11974         'remove' : true,
11975         /**
11976          * @event afterremove
11977          * Fires when the remove value from the combobox array
11978              * @param {Roo.bootstrap.ComboBox} combo This combo box
11979              */
11980         'afterremove' : true,
11981         /**
11982          * @event specialfilter
11983          * Fires when specialfilter
11984             * @param {Roo.bootstrap.ComboBox} combo This combo box
11985             */
11986         'specialfilter' : true,
11987         /**
11988          * @event tick
11989          * Fires when tick the element
11990             * @param {Roo.bootstrap.ComboBox} combo This combo box
11991             */
11992         'tick' : true,
11993         /**
11994          * @event touchviewdisplay
11995          * Fires when touch view require special display (default is using displayField)
11996             * @param {Roo.bootstrap.ComboBox} combo This combo box
11997             * @param {Object} cfg set html .
11998             */
11999         'touchviewdisplay' : true
12000         
12001     });
12002     
12003     this.item = [];
12004     this.tickItems = [];
12005     
12006     this.selectedIndex = -1;
12007     if(this.mode == 'local'){
12008         if(config.queryDelay === undefined){
12009             this.queryDelay = 10;
12010         }
12011         if(config.minChars === undefined){
12012             this.minChars = 0;
12013         }
12014     }
12015 };
12016
12017 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12018      
12019     /**
12020      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12021      * rendering into an Roo.Editor, defaults to false)
12022      */
12023     /**
12024      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12025      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12026      */
12027     /**
12028      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12029      */
12030     /**
12031      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12032      * the dropdown list (defaults to undefined, with no header element)
12033      */
12034
12035      /**
12036      * @cfg {String/Roo.Template} tpl The template to use to render the output
12037      */
12038      
12039      /**
12040      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12041      */
12042     listWidth: undefined,
12043     /**
12044      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12045      * mode = 'remote' or 'text' if mode = 'local')
12046      */
12047     displayField: undefined,
12048     
12049     /**
12050      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12051      * mode = 'remote' or 'value' if mode = 'local'). 
12052      * Note: use of a valueField requires the user make a selection
12053      * in order for a value to be mapped.
12054      */
12055     valueField: undefined,
12056     /**
12057      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12058      */
12059     modalTitle : '',
12060     
12061     /**
12062      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12063      * field's data value (defaults to the underlying DOM element's name)
12064      */
12065     hiddenName: undefined,
12066     /**
12067      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12068      */
12069     listClass: '',
12070     /**
12071      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12072      */
12073     selectedClass: 'active',
12074     
12075     /**
12076      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12077      */
12078     shadow:'sides',
12079     /**
12080      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12081      * anchor positions (defaults to 'tl-bl')
12082      */
12083     listAlign: 'tl-bl?',
12084     /**
12085      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12086      */
12087     maxHeight: 300,
12088     /**
12089      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12090      * query specified by the allQuery config option (defaults to 'query')
12091      */
12092     triggerAction: 'query',
12093     /**
12094      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12095      * (defaults to 4, does not apply if editable = false)
12096      */
12097     minChars : 4,
12098     /**
12099      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12100      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12101      */
12102     typeAhead: false,
12103     /**
12104      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12105      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12106      */
12107     queryDelay: 500,
12108     /**
12109      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12110      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12111      */
12112     pageSize: 0,
12113     /**
12114      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12115      * when editable = true (defaults to false)
12116      */
12117     selectOnFocus:false,
12118     /**
12119      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12120      */
12121     queryParam: 'query',
12122     /**
12123      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12124      * when mode = 'remote' (defaults to 'Loading...')
12125      */
12126     loadingText: 'Loading...',
12127     /**
12128      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12129      */
12130     resizable: false,
12131     /**
12132      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12133      */
12134     handleHeight : 8,
12135     /**
12136      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12137      * traditional select (defaults to true)
12138      */
12139     editable: true,
12140     /**
12141      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12142      */
12143     allQuery: '',
12144     /**
12145      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12146      */
12147     mode: 'remote',
12148     /**
12149      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12150      * listWidth has a higher value)
12151      */
12152     minListWidth : 70,
12153     /**
12154      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12155      * allow the user to set arbitrary text into the field (defaults to false)
12156      */
12157     forceSelection:false,
12158     /**
12159      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12160      * if typeAhead = true (defaults to 250)
12161      */
12162     typeAheadDelay : 250,
12163     /**
12164      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12165      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12166      */
12167     valueNotFoundText : undefined,
12168     /**
12169      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12170      */
12171     blockFocus : false,
12172     
12173     /**
12174      * @cfg {Boolean} disableClear Disable showing of clear button.
12175      */
12176     disableClear : false,
12177     /**
12178      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12179      */
12180     alwaysQuery : false,
12181     
12182     /**
12183      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12184      */
12185     multiple : false,
12186     
12187     /**
12188      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12189      */
12190     invalidClass : "has-warning",
12191     
12192     /**
12193      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12194      */
12195     validClass : "has-success",
12196     
12197     /**
12198      * @cfg {Boolean} specialFilter (true|false) special filter default false
12199      */
12200     specialFilter : false,
12201     
12202     /**
12203      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12204      */
12205     mobileTouchView : true,
12206     
12207     //private
12208     addicon : false,
12209     editicon: false,
12210     
12211     page: 0,
12212     hasQuery: false,
12213     append: false,
12214     loadNext: false,
12215     autoFocus : true,
12216     tickable : false,
12217     btnPosition : 'right',
12218     triggerList : true,
12219     showToggleBtn : true,
12220     animate : true,
12221     emptyResultText: 'Empty',
12222     triggerText : 'Select',
12223     
12224     // element that contains real text value.. (when hidden is used..)
12225     
12226     getAutoCreate : function()
12227     {
12228         var cfg = false;
12229         
12230         /*
12231          * Touch Devices
12232          */
12233         
12234         if(Roo.isTouch && this.mobileTouchView){
12235             cfg = this.getAutoCreateTouchView();
12236             return cfg;;
12237         }
12238         
12239         /*
12240          *  Normal ComboBox
12241          */
12242         if(!this.tickable){
12243             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12244             return cfg;
12245         }
12246         
12247         /*
12248          *  ComboBox with tickable selections
12249          */
12250              
12251         var align = this.labelAlign || this.parentLabelAlign();
12252         
12253         cfg = {
12254             cls : 'form-group roo-combobox-tickable' //input-group
12255         };
12256         
12257         var buttons = {
12258             tag : 'div',
12259             cls : 'tickable-buttons',
12260             cn : [
12261                 {
12262                     tag : 'button',
12263                     type : 'button',
12264                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12265                     html : this.triggerText
12266                 },
12267                 {
12268                     tag : 'button',
12269                     type : 'button',
12270                     name : 'ok',
12271                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12272                     html : 'Done'
12273                 },
12274                 {
12275                     tag : 'button',
12276                     type : 'button',
12277                     name : 'cancel',
12278                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12279                     html : 'Cancel'
12280                 }
12281             ]
12282         };
12283         
12284         if(this.editable){
12285             buttons.cn.unshift({
12286                 tag: 'input',
12287                 cls: 'roo-select2-search-field-input'
12288             });
12289         }
12290         
12291         var _this = this;
12292         
12293         Roo.each(buttons.cn, function(c){
12294             if (_this.size) {
12295                 c.cls += ' btn-' + _this.size;
12296             }
12297
12298             if (_this.disabled) {
12299                 c.disabled = true;
12300             }
12301         });
12302         
12303         var box = {
12304             tag: 'div',
12305             cn: [
12306                 {
12307                     tag: 'input',
12308                     type : 'hidden',
12309                     cls: 'form-hidden-field'
12310                 },
12311                 {
12312                     tag: 'ul',
12313                     cls: 'roo-select2-choices',
12314                     cn:[
12315                         {
12316                             tag: 'li',
12317                             cls: 'roo-select2-search-field',
12318                             cn: [
12319
12320                                 buttons
12321                             ]
12322                         }
12323                     ]
12324                 }
12325             ]
12326         };
12327         
12328         var combobox = {
12329             cls: 'roo-select2-container input-group roo-select2-container-multi',
12330             cn: [
12331                 box
12332 //                {
12333 //                    tag: 'ul',
12334 //                    cls: 'typeahead typeahead-long dropdown-menu',
12335 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12336 //                }
12337             ]
12338         };
12339         
12340         if(this.hasFeedback && !this.allowBlank){
12341             
12342             var feedback = {
12343                 tag: 'span',
12344                 cls: 'glyphicon form-control-feedback'
12345             };
12346
12347             combobox.cn.push(feedback);
12348         }
12349         
12350         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12351             
12352 //                Roo.log("left and has label");
12353             cfg.cn = [
12354                 {
12355                     tag : 'i',
12356                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12357                     tooltip : 'This field is required'
12358                 },
12359                 {
12360                     tag: 'label',
12361                     'for' :  id,
12362                     cls : 'control-label col-sm-' + this.labelWidth,
12363                     html : this.fieldLabel
12364
12365                 },
12366                 {
12367                     cls : "col-sm-" + (12 - this.labelWidth), 
12368                     cn: [
12369                         combobox
12370                     ]
12371                 }
12372
12373             ];
12374
12375             if(this.indicatorpos == 'right'){
12376                 
12377                 cfg.cn = [
12378                     {
12379                         tag: 'label',
12380                         'for' :  id,
12381                         cls : 'control-label col-sm-' + this.labelWidth,
12382                         html : this.fieldLabel
12383
12384                     },
12385                     {
12386                         tag : 'i',
12387                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12388                         tooltip : 'This field is required'
12389                     },
12390                     {
12391                         cls : "col-sm-" + (12 - this.labelWidth), 
12392                         cn: [
12393                             combobox
12394                         ]
12395                     }
12396
12397                 ];
12398             
12399             }
12400                 
12401                 
12402         } else if ( this.fieldLabel.length) {
12403 //                Roo.log(" label");
12404                  cfg.cn = [
12405                     {
12406                         tag : 'i',
12407                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12408                         tooltip : 'This field is required'
12409                     },
12410                     {
12411                         tag: 'label',
12412                         //cls : 'input-group-addon',
12413                         html : this.fieldLabel
12414                         
12415                     },
12416                     
12417                     combobox
12418                     
12419                 ];
12420                 
12421                 if(this.indicatorpos == 'right'){
12422                     
12423                     cfg.cn = [
12424                         {
12425                             tag: 'label',
12426                             //cls : 'input-group-addon',
12427                             html : this.fieldLabel
12428
12429                         },
12430                         
12431                         {
12432                             tag : 'i',
12433                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12434                             tooltip : 'This field is required'
12435                         },
12436                         
12437                         combobox
12438
12439                     ];
12440                 
12441                 }
12442
12443         } else {
12444             
12445 //                Roo.log(" no label && no align");
12446                 cfg = combobox
12447                      
12448                 
12449         }
12450          
12451         var settings=this;
12452         ['xs','sm','md','lg'].map(function(size){
12453             if (settings[size]) {
12454                 cfg.cls += ' col-' + size + '-' + settings[size];
12455             }
12456         });
12457         
12458         return cfg;
12459         
12460     },
12461     
12462     _initEventsCalled : false,
12463     
12464     // private
12465     initEvents: function()
12466     {
12467         
12468         if (this._initEventsCalled) { // as we call render... prevent looping...
12469             return;
12470         }
12471         this._initEventsCalled = true;
12472         
12473         if (!this.store) {
12474             throw "can not find store for combo";
12475         }
12476         
12477         this.store = Roo.factory(this.store, Roo.data);
12478         
12479         // if we are building from html. then this element is so complex, that we can not really
12480         // use the rendered HTML.
12481         // so we have to trash and replace the previous code.
12482         if (Roo.XComponent.build_from_html) {
12483             
12484             // remove this element....
12485             var e = this.el.dom, k=0;
12486             while (e ) { e = e.previousSibling;  ++k;}
12487
12488             this.el.remove();
12489             
12490             this.el=false;
12491             this.rendered = false;
12492             
12493             this.render(this.parent().getChildContainer(true), k);
12494             
12495             
12496             
12497         }
12498         
12499         
12500         /*
12501          * Touch Devices
12502          */
12503         
12504         if(Roo.isTouch && this.mobileTouchView){
12505             this.initTouchView();
12506             return;
12507         }
12508         
12509         if(this.tickable){
12510             this.initTickableEvents();
12511             return;
12512         }
12513         
12514         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12515         
12516         if(this.hiddenName){
12517             
12518             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12519             
12520             this.hiddenField.dom.value =
12521                 this.hiddenValue !== undefined ? this.hiddenValue :
12522                 this.value !== undefined ? this.value : '';
12523
12524             // prevent input submission
12525             this.el.dom.removeAttribute('name');
12526             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12527              
12528              
12529         }
12530         //if(Roo.isGecko){
12531         //    this.el.dom.setAttribute('autocomplete', 'off');
12532         //}
12533         
12534         var cls = 'x-combo-list';
12535         
12536         //this.list = new Roo.Layer({
12537         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12538         //});
12539         
12540         var _this = this;
12541         
12542         (function(){
12543             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12544             _this.list.setWidth(lw);
12545         }).defer(100);
12546         
12547         this.list.on('mouseover', this.onViewOver, this);
12548         this.list.on('mousemove', this.onViewMove, this);
12549         
12550         this.list.on('scroll', this.onViewScroll, this);
12551         
12552         /*
12553         this.list.swallowEvent('mousewheel');
12554         this.assetHeight = 0;
12555
12556         if(this.title){
12557             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12558             this.assetHeight += this.header.getHeight();
12559         }
12560
12561         this.innerList = this.list.createChild({cls:cls+'-inner'});
12562         this.innerList.on('mouseover', this.onViewOver, this);
12563         this.innerList.on('mousemove', this.onViewMove, this);
12564         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12565         
12566         if(this.allowBlank && !this.pageSize && !this.disableClear){
12567             this.footer = this.list.createChild({cls:cls+'-ft'});
12568             this.pageTb = new Roo.Toolbar(this.footer);
12569            
12570         }
12571         if(this.pageSize){
12572             this.footer = this.list.createChild({cls:cls+'-ft'});
12573             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12574                     {pageSize: this.pageSize});
12575             
12576         }
12577         
12578         if (this.pageTb && this.allowBlank && !this.disableClear) {
12579             var _this = this;
12580             this.pageTb.add(new Roo.Toolbar.Fill(), {
12581                 cls: 'x-btn-icon x-btn-clear',
12582                 text: '&#160;',
12583                 handler: function()
12584                 {
12585                     _this.collapse();
12586                     _this.clearValue();
12587                     _this.onSelect(false, -1);
12588                 }
12589             });
12590         }
12591         if (this.footer) {
12592             this.assetHeight += this.footer.getHeight();
12593         }
12594         */
12595             
12596         if(!this.tpl){
12597             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12598         }
12599
12600         this.view = new Roo.View(this.list, this.tpl, {
12601             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12602         });
12603         //this.view.wrapEl.setDisplayed(false);
12604         this.view.on('click', this.onViewClick, this);
12605         
12606         
12607         
12608         this.store.on('beforeload', this.onBeforeLoad, this);
12609         this.store.on('load', this.onLoad, this);
12610         this.store.on('loadexception', this.onLoadException, this);
12611         /*
12612         if(this.resizable){
12613             this.resizer = new Roo.Resizable(this.list,  {
12614                pinned:true, handles:'se'
12615             });
12616             this.resizer.on('resize', function(r, w, h){
12617                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12618                 this.listWidth = w;
12619                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12620                 this.restrictHeight();
12621             }, this);
12622             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12623         }
12624         */
12625         if(!this.editable){
12626             this.editable = true;
12627             this.setEditable(false);
12628         }
12629         
12630         /*
12631         
12632         if (typeof(this.events.add.listeners) != 'undefined') {
12633             
12634             this.addicon = this.wrap.createChild(
12635                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12636        
12637             this.addicon.on('click', function(e) {
12638                 this.fireEvent('add', this);
12639             }, this);
12640         }
12641         if (typeof(this.events.edit.listeners) != 'undefined') {
12642             
12643             this.editicon = this.wrap.createChild(
12644                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12645             if (this.addicon) {
12646                 this.editicon.setStyle('margin-left', '40px');
12647             }
12648             this.editicon.on('click', function(e) {
12649                 
12650                 // we fire even  if inothing is selected..
12651                 this.fireEvent('edit', this, this.lastData );
12652                 
12653             }, this);
12654         }
12655         */
12656         
12657         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12658             "up" : function(e){
12659                 this.inKeyMode = true;
12660                 this.selectPrev();
12661             },
12662
12663             "down" : function(e){
12664                 if(!this.isExpanded()){
12665                     this.onTriggerClick();
12666                 }else{
12667                     this.inKeyMode = true;
12668                     this.selectNext();
12669                 }
12670             },
12671
12672             "enter" : function(e){
12673 //                this.onViewClick();
12674                 //return true;
12675                 this.collapse();
12676                 
12677                 if(this.fireEvent("specialkey", this, e)){
12678                     this.onViewClick(false);
12679                 }
12680                 
12681                 return true;
12682             },
12683
12684             "esc" : function(e){
12685                 this.collapse();
12686             },
12687
12688             "tab" : function(e){
12689                 this.collapse();
12690                 
12691                 if(this.fireEvent("specialkey", this, e)){
12692                     this.onViewClick(false);
12693                 }
12694                 
12695                 return true;
12696             },
12697
12698             scope : this,
12699
12700             doRelay : function(foo, bar, hname){
12701                 if(hname == 'down' || this.scope.isExpanded()){
12702                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12703                 }
12704                 return true;
12705             },
12706
12707             forceKeyDown: true
12708         });
12709         
12710         
12711         this.queryDelay = Math.max(this.queryDelay || 10,
12712                 this.mode == 'local' ? 10 : 250);
12713         
12714         
12715         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12716         
12717         if(this.typeAhead){
12718             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12719         }
12720         if(this.editable !== false){
12721             this.inputEl().on("keyup", this.onKeyUp, this);
12722         }
12723         if(this.forceSelection){
12724             this.inputEl().on('blur', this.doForce, this);
12725         }
12726         
12727         if(this.multiple){
12728             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12729             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12730         }
12731     },
12732     
12733     initTickableEvents: function()
12734     {   
12735         this.createList();
12736         
12737         if(this.hiddenName){
12738             
12739             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12740             
12741             this.hiddenField.dom.value =
12742                 this.hiddenValue !== undefined ? this.hiddenValue :
12743                 this.value !== undefined ? this.value : '';
12744
12745             // prevent input submission
12746             this.el.dom.removeAttribute('name');
12747             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12748              
12749              
12750         }
12751         
12752 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12753         
12754         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12755         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12756         if(this.triggerList){
12757             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12758         }
12759          
12760         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12761         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12762         
12763         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12764         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12765         
12766         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12767         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12768         
12769         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12770         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12771         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12772         
12773         this.okBtn.hide();
12774         this.cancelBtn.hide();
12775         
12776         var _this = this;
12777         
12778         (function(){
12779             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12780             _this.list.setWidth(lw);
12781         }).defer(100);
12782         
12783         this.list.on('mouseover', this.onViewOver, this);
12784         this.list.on('mousemove', this.onViewMove, this);
12785         
12786         this.list.on('scroll', this.onViewScroll, this);
12787         
12788         if(!this.tpl){
12789             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>';
12790         }
12791
12792         this.view = new Roo.View(this.list, this.tpl, {
12793             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12794         });
12795         
12796         //this.view.wrapEl.setDisplayed(false);
12797         this.view.on('click', this.onViewClick, this);
12798         
12799         
12800         
12801         this.store.on('beforeload', this.onBeforeLoad, this);
12802         this.store.on('load', this.onLoad, this);
12803         this.store.on('loadexception', this.onLoadException, this);
12804         
12805         if(this.editable){
12806             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12807                 "up" : function(e){
12808                     this.inKeyMode = true;
12809                     this.selectPrev();
12810                 },
12811
12812                 "down" : function(e){
12813                     this.inKeyMode = true;
12814                     this.selectNext();
12815                 },
12816
12817                 "enter" : function(e){
12818                     if(this.fireEvent("specialkey", this, e)){
12819                         this.onViewClick(false);
12820                     }
12821                     
12822                     return true;
12823                 },
12824
12825                 "esc" : function(e){
12826                     this.onTickableFooterButtonClick(e, false, false);
12827                 },
12828
12829                 "tab" : function(e){
12830                     this.fireEvent("specialkey", this, e);
12831                     
12832                     this.onTickableFooterButtonClick(e, false, false);
12833                     
12834                     return true;
12835                 },
12836
12837                 scope : this,
12838
12839                 doRelay : function(e, fn, key){
12840                     if(this.scope.isExpanded()){
12841                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12842                     }
12843                     return true;
12844                 },
12845
12846                 forceKeyDown: true
12847             });
12848         }
12849         
12850         this.queryDelay = Math.max(this.queryDelay || 10,
12851                 this.mode == 'local' ? 10 : 250);
12852         
12853         
12854         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12855         
12856         if(this.typeAhead){
12857             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12858         }
12859         
12860         if(this.editable !== false){
12861             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12862         }
12863         
12864     },
12865
12866     onDestroy : function(){
12867         if(this.view){
12868             this.view.setStore(null);
12869             this.view.el.removeAllListeners();
12870             this.view.el.remove();
12871             this.view.purgeListeners();
12872         }
12873         if(this.list){
12874             this.list.dom.innerHTML  = '';
12875         }
12876         
12877         if(this.store){
12878             this.store.un('beforeload', this.onBeforeLoad, this);
12879             this.store.un('load', this.onLoad, this);
12880             this.store.un('loadexception', this.onLoadException, this);
12881         }
12882         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12883     },
12884
12885     // private
12886     fireKey : function(e){
12887         if(e.isNavKeyPress() && !this.list.isVisible()){
12888             this.fireEvent("specialkey", this, e);
12889         }
12890     },
12891
12892     // private
12893     onResize: function(w, h){
12894 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12895 //        
12896 //        if(typeof w != 'number'){
12897 //            // we do not handle it!?!?
12898 //            return;
12899 //        }
12900 //        var tw = this.trigger.getWidth();
12901 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12902 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12903 //        var x = w - tw;
12904 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12905 //            
12906 //        //this.trigger.setStyle('left', x+'px');
12907 //        
12908 //        if(this.list && this.listWidth === undefined){
12909 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12910 //            this.list.setWidth(lw);
12911 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12912 //        }
12913         
12914     
12915         
12916     },
12917
12918     /**
12919      * Allow or prevent the user from directly editing the field text.  If false is passed,
12920      * the user will only be able to select from the items defined in the dropdown list.  This method
12921      * is the runtime equivalent of setting the 'editable' config option at config time.
12922      * @param {Boolean} value True to allow the user to directly edit the field text
12923      */
12924     setEditable : function(value){
12925         if(value == this.editable){
12926             return;
12927         }
12928         this.editable = value;
12929         if(!value){
12930             this.inputEl().dom.setAttribute('readOnly', true);
12931             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12932             this.inputEl().addClass('x-combo-noedit');
12933         }else{
12934             this.inputEl().dom.setAttribute('readOnly', false);
12935             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12936             this.inputEl().removeClass('x-combo-noedit');
12937         }
12938     },
12939
12940     // private
12941     
12942     onBeforeLoad : function(combo,opts){
12943         if(!this.hasFocus){
12944             return;
12945         }
12946          if (!opts.add) {
12947             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12948          }
12949         this.restrictHeight();
12950         this.selectedIndex = -1;
12951     },
12952
12953     // private
12954     onLoad : function(){
12955         
12956         this.hasQuery = false;
12957         
12958         if(!this.hasFocus){
12959             return;
12960         }
12961         
12962         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12963             this.loading.hide();
12964         }
12965              
12966         if(this.store.getCount() > 0){
12967             this.expand();
12968             this.restrictHeight();
12969             if(this.lastQuery == this.allQuery){
12970                 if(this.editable && !this.tickable){
12971                     this.inputEl().dom.select();
12972                 }
12973                 
12974                 if(
12975                     !this.selectByValue(this.value, true) &&
12976                     this.autoFocus && 
12977                     (
12978                         !this.store.lastOptions ||
12979                         typeof(this.store.lastOptions.add) == 'undefined' || 
12980                         this.store.lastOptions.add != true
12981                     )
12982                 ){
12983                     this.select(0, true);
12984                 }
12985             }else{
12986                 if(this.autoFocus){
12987                     this.selectNext();
12988                 }
12989                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12990                     this.taTask.delay(this.typeAheadDelay);
12991                 }
12992             }
12993         }else{
12994             this.onEmptyResults();
12995         }
12996         
12997         //this.el.focus();
12998     },
12999     // private
13000     onLoadException : function()
13001     {
13002         this.hasQuery = false;
13003         
13004         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13005             this.loading.hide();
13006         }
13007         
13008         if(this.tickable && this.editable){
13009             return;
13010         }
13011         
13012         this.collapse();
13013         // only causes errors at present
13014         //Roo.log(this.store.reader.jsonData);
13015         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13016             // fixme
13017             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13018         //}
13019         
13020         
13021     },
13022     // private
13023     onTypeAhead : function(){
13024         if(this.store.getCount() > 0){
13025             var r = this.store.getAt(0);
13026             var newValue = r.data[this.displayField];
13027             var len = newValue.length;
13028             var selStart = this.getRawValue().length;
13029             
13030             if(selStart != len){
13031                 this.setRawValue(newValue);
13032                 this.selectText(selStart, newValue.length);
13033             }
13034         }
13035     },
13036
13037     // private
13038     onSelect : function(record, index){
13039         
13040         if(this.fireEvent('beforeselect', this, record, index) !== false){
13041         
13042             this.setFromData(index > -1 ? record.data : false);
13043             
13044             this.collapse();
13045             this.fireEvent('select', this, record, index);
13046         }
13047     },
13048
13049     /**
13050      * Returns the currently selected field value or empty string if no value is set.
13051      * @return {String} value The selected value
13052      */
13053     getValue : function(){
13054         
13055         if(this.multiple){
13056             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13057         }
13058         
13059         if(this.valueField){
13060             return typeof this.value != 'undefined' ? this.value : '';
13061         }else{
13062             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13063         }
13064     },
13065
13066     /**
13067      * Clears any text/value currently set in the field
13068      */
13069     clearValue : function(){
13070         if(this.hiddenField){
13071             this.hiddenField.dom.value = '';
13072         }
13073         this.value = '';
13074         this.setRawValue('');
13075         this.lastSelectionText = '';
13076         this.lastData = false;
13077         
13078         var close = this.closeTriggerEl();
13079         
13080         if(close){
13081             close.hide();
13082         }
13083         
13084         this.validate();
13085         
13086     },
13087
13088     /**
13089      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13090      * will be displayed in the field.  If the value does not match the data value of an existing item,
13091      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13092      * Otherwise the field will be blank (although the value will still be set).
13093      * @param {String} value The value to match
13094      */
13095     setValue : function(v){
13096         if(this.multiple){
13097             this.syncValue();
13098             return;
13099         }
13100         
13101         var text = v;
13102         if(this.valueField){
13103             var r = this.findRecord(this.valueField, v);
13104             if(r){
13105                 text = r.data[this.displayField];
13106             }else if(this.valueNotFoundText !== undefined){
13107                 text = this.valueNotFoundText;
13108             }
13109         }
13110         this.lastSelectionText = text;
13111         if(this.hiddenField){
13112             this.hiddenField.dom.value = v;
13113         }
13114         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13115         this.value = v;
13116         
13117         var close = this.closeTriggerEl();
13118         
13119         if(close){
13120             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13121         }
13122         
13123         this.validate();
13124     },
13125     /**
13126      * @property {Object} the last set data for the element
13127      */
13128     
13129     lastData : false,
13130     /**
13131      * Sets the value of the field based on a object which is related to the record format for the store.
13132      * @param {Object} value the value to set as. or false on reset?
13133      */
13134     setFromData : function(o){
13135         
13136         if(this.multiple){
13137             this.addItem(o);
13138             return;
13139         }
13140             
13141         var dv = ''; // display value
13142         var vv = ''; // value value..
13143         this.lastData = o;
13144         if (this.displayField) {
13145             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13146         } else {
13147             // this is an error condition!!!
13148             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13149         }
13150         
13151         if(this.valueField){
13152             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13153         }
13154         
13155         var close = this.closeTriggerEl();
13156         
13157         if(close){
13158             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13159         }
13160         
13161         if(this.hiddenField){
13162             this.hiddenField.dom.value = vv;
13163             
13164             this.lastSelectionText = dv;
13165             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13166             this.value = vv;
13167             return;
13168         }
13169         // no hidden field.. - we store the value in 'value', but still display
13170         // display field!!!!
13171         this.lastSelectionText = dv;
13172         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13173         this.value = vv;
13174         
13175         
13176         
13177     },
13178     // private
13179     reset : function(){
13180         // overridden so that last data is reset..
13181         
13182         if(this.multiple){
13183             this.clearItem();
13184             return;
13185         }
13186         
13187         this.setValue(this.originalValue);
13188         //this.clearInvalid();
13189         this.lastData = false;
13190         if (this.view) {
13191             this.view.clearSelections();
13192         }
13193         
13194         this.validate();
13195     },
13196     // private
13197     findRecord : function(prop, value){
13198         var record;
13199         if(this.store.getCount() > 0){
13200             this.store.each(function(r){
13201                 if(r.data[prop] == value){
13202                     record = r;
13203                     return false;
13204                 }
13205                 return true;
13206             });
13207         }
13208         return record;
13209     },
13210     
13211     getName: function()
13212     {
13213         // returns hidden if it's set..
13214         if (!this.rendered) {return ''};
13215         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13216         
13217     },
13218     // private
13219     onViewMove : function(e, t){
13220         this.inKeyMode = false;
13221     },
13222
13223     // private
13224     onViewOver : function(e, t){
13225         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13226             return;
13227         }
13228         var item = this.view.findItemFromChild(t);
13229         
13230         if(item){
13231             var index = this.view.indexOf(item);
13232             this.select(index, false);
13233         }
13234     },
13235
13236     // private
13237     onViewClick : function(view, doFocus, el, e)
13238     {
13239         var index = this.view.getSelectedIndexes()[0];
13240         
13241         var r = this.store.getAt(index);
13242         
13243         if(this.tickable){
13244             
13245             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13246                 return;
13247             }
13248             
13249             var rm = false;
13250             var _this = this;
13251             
13252             Roo.each(this.tickItems, function(v,k){
13253                 
13254                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13255                     Roo.log(v);
13256                     _this.tickItems.splice(k, 1);
13257                     
13258                     if(typeof(e) == 'undefined' && view == false){
13259                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13260                     }
13261                     
13262                     rm = true;
13263                     return;
13264                 }
13265             });
13266             
13267             if(rm){
13268                 return;
13269             }
13270             
13271             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13272                 this.tickItems.push(r.data);
13273             }
13274             
13275             if(typeof(e) == 'undefined' && view == false){
13276                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13277             }
13278                     
13279             return;
13280         }
13281         
13282         if(r){
13283             this.onSelect(r, index);
13284         }
13285         if(doFocus !== false && !this.blockFocus){
13286             this.inputEl().focus();
13287         }
13288     },
13289
13290     // private
13291     restrictHeight : function(){
13292         //this.innerList.dom.style.height = '';
13293         //var inner = this.innerList.dom;
13294         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13295         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13296         //this.list.beginUpdate();
13297         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13298         this.list.alignTo(this.inputEl(), this.listAlign);
13299         this.list.alignTo(this.inputEl(), this.listAlign);
13300         //this.list.endUpdate();
13301     },
13302
13303     // private
13304     onEmptyResults : function(){
13305         
13306         if(this.tickable && this.editable){
13307             this.restrictHeight();
13308             return;
13309         }
13310         
13311         this.collapse();
13312     },
13313
13314     /**
13315      * Returns true if the dropdown list is expanded, else false.
13316      */
13317     isExpanded : function(){
13318         return this.list.isVisible();
13319     },
13320
13321     /**
13322      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13323      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13324      * @param {String} value The data value of the item to select
13325      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13326      * selected item if it is not currently in view (defaults to true)
13327      * @return {Boolean} True if the value matched an item in the list, else false
13328      */
13329     selectByValue : function(v, scrollIntoView){
13330         if(v !== undefined && v !== null){
13331             var r = this.findRecord(this.valueField || this.displayField, v);
13332             if(r){
13333                 this.select(this.store.indexOf(r), scrollIntoView);
13334                 return true;
13335             }
13336         }
13337         return false;
13338     },
13339
13340     /**
13341      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13342      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13343      * @param {Number} index The zero-based index of the list item to select
13344      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13345      * selected item if it is not currently in view (defaults to true)
13346      */
13347     select : function(index, scrollIntoView){
13348         this.selectedIndex = index;
13349         this.view.select(index);
13350         if(scrollIntoView !== false){
13351             var el = this.view.getNode(index);
13352             /*
13353              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13354              */
13355             if(el){
13356                 this.list.scrollChildIntoView(el, false);
13357             }
13358         }
13359     },
13360
13361     // private
13362     selectNext : function(){
13363         var ct = this.store.getCount();
13364         if(ct > 0){
13365             if(this.selectedIndex == -1){
13366                 this.select(0);
13367             }else if(this.selectedIndex < ct-1){
13368                 this.select(this.selectedIndex+1);
13369             }
13370         }
13371     },
13372
13373     // private
13374     selectPrev : function(){
13375         var ct = this.store.getCount();
13376         if(ct > 0){
13377             if(this.selectedIndex == -1){
13378                 this.select(0);
13379             }else if(this.selectedIndex != 0){
13380                 this.select(this.selectedIndex-1);
13381             }
13382         }
13383     },
13384
13385     // private
13386     onKeyUp : function(e){
13387         if(this.editable !== false && !e.isSpecialKey()){
13388             this.lastKey = e.getKey();
13389             this.dqTask.delay(this.queryDelay);
13390         }
13391     },
13392
13393     // private
13394     validateBlur : function(){
13395         return !this.list || !this.list.isVisible();   
13396     },
13397
13398     // private
13399     initQuery : function(){
13400         
13401         var v = this.getRawValue();
13402         
13403         if(this.tickable && this.editable){
13404             v = this.tickableInputEl().getValue();
13405         }
13406         
13407         this.doQuery(v);
13408     },
13409
13410     // private
13411     doForce : function(){
13412         if(this.inputEl().dom.value.length > 0){
13413             this.inputEl().dom.value =
13414                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13415              
13416         }
13417     },
13418
13419     /**
13420      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13421      * query allowing the query action to be canceled if needed.
13422      * @param {String} query The SQL query to execute
13423      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13424      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13425      * saved in the current store (defaults to false)
13426      */
13427     doQuery : function(q, forceAll){
13428         
13429         if(q === undefined || q === null){
13430             q = '';
13431         }
13432         var qe = {
13433             query: q,
13434             forceAll: forceAll,
13435             combo: this,
13436             cancel:false
13437         };
13438         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13439             return false;
13440         }
13441         q = qe.query;
13442         
13443         forceAll = qe.forceAll;
13444         if(forceAll === true || (q.length >= this.minChars)){
13445             
13446             this.hasQuery = true;
13447             
13448             if(this.lastQuery != q || this.alwaysQuery){
13449                 this.lastQuery = q;
13450                 if(this.mode == 'local'){
13451                     this.selectedIndex = -1;
13452                     if(forceAll){
13453                         this.store.clearFilter();
13454                     }else{
13455                         
13456                         if(this.specialFilter){
13457                             this.fireEvent('specialfilter', this);
13458                             this.onLoad();
13459                             return;
13460                         }
13461                         
13462                         this.store.filter(this.displayField, q);
13463                     }
13464                     
13465                     this.store.fireEvent("datachanged", this.store);
13466                     
13467                     this.onLoad();
13468                     
13469                     
13470                 }else{
13471                     
13472                     this.store.baseParams[this.queryParam] = q;
13473                     
13474                     var options = {params : this.getParams(q)};
13475                     
13476                     if(this.loadNext){
13477                         options.add = true;
13478                         options.params.start = this.page * this.pageSize;
13479                     }
13480                     
13481                     this.store.load(options);
13482                     
13483                     /*
13484                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13485                      *  we should expand the list on onLoad
13486                      *  so command out it
13487                      */
13488 //                    this.expand();
13489                 }
13490             }else{
13491                 this.selectedIndex = -1;
13492                 this.onLoad();   
13493             }
13494         }
13495         
13496         this.loadNext = false;
13497     },
13498     
13499     // private
13500     getParams : function(q){
13501         var p = {};
13502         //p[this.queryParam] = q;
13503         
13504         if(this.pageSize){
13505             p.start = 0;
13506             p.limit = this.pageSize;
13507         }
13508         return p;
13509     },
13510
13511     /**
13512      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13513      */
13514     collapse : function(){
13515         if(!this.isExpanded()){
13516             return;
13517         }
13518         
13519         this.list.hide();
13520         
13521         if(this.tickable){
13522             this.hasFocus = false;
13523             this.okBtn.hide();
13524             this.cancelBtn.hide();
13525             this.trigger.show();
13526             
13527             if(this.editable){
13528                 this.tickableInputEl().dom.value = '';
13529                 this.tickableInputEl().blur();
13530             }
13531             
13532         }
13533         
13534         Roo.get(document).un('mousedown', this.collapseIf, this);
13535         Roo.get(document).un('mousewheel', this.collapseIf, this);
13536         if (!this.editable) {
13537             Roo.get(document).un('keydown', this.listKeyPress, this);
13538         }
13539         this.fireEvent('collapse', this);
13540         
13541         this.validate();
13542     },
13543
13544     // private
13545     collapseIf : function(e){
13546         var in_combo  = e.within(this.el);
13547         var in_list =  e.within(this.list);
13548         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13549         
13550         if (in_combo || in_list || is_list) {
13551             //e.stopPropagation();
13552             return;
13553         }
13554         
13555         if(this.tickable){
13556             this.onTickableFooterButtonClick(e, false, false);
13557         }
13558
13559         this.collapse();
13560         
13561     },
13562
13563     /**
13564      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13565      */
13566     expand : function(){
13567        
13568         if(this.isExpanded() || !this.hasFocus){
13569             return;
13570         }
13571         
13572         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13573         this.list.setWidth(lw);
13574         
13575         
13576          Roo.log('expand');
13577         
13578         this.list.show();
13579         
13580         this.restrictHeight();
13581         
13582         if(this.tickable){
13583             
13584             this.tickItems = Roo.apply([], this.item);
13585             
13586             this.okBtn.show();
13587             this.cancelBtn.show();
13588             this.trigger.hide();
13589             
13590             if(this.editable){
13591                 this.tickableInputEl().focus();
13592             }
13593             
13594         }
13595         
13596         Roo.get(document).on('mousedown', this.collapseIf, this);
13597         Roo.get(document).on('mousewheel', this.collapseIf, this);
13598         if (!this.editable) {
13599             Roo.get(document).on('keydown', this.listKeyPress, this);
13600         }
13601         
13602         this.fireEvent('expand', this);
13603     },
13604
13605     // private
13606     // Implements the default empty TriggerField.onTriggerClick function
13607     onTriggerClick : function(e)
13608     {
13609         Roo.log('trigger click');
13610         
13611         if(this.disabled || !this.triggerList){
13612             return;
13613         }
13614         
13615         this.page = 0;
13616         this.loadNext = false;
13617         
13618         if(this.isExpanded()){
13619             this.collapse();
13620             if (!this.blockFocus) {
13621                 this.inputEl().focus();
13622             }
13623             
13624         }else {
13625             this.hasFocus = true;
13626             if(this.triggerAction == 'all') {
13627                 this.doQuery(this.allQuery, true);
13628             } else {
13629                 this.doQuery(this.getRawValue());
13630             }
13631             if (!this.blockFocus) {
13632                 this.inputEl().focus();
13633             }
13634         }
13635     },
13636     
13637     onTickableTriggerClick : function(e)
13638     {
13639         if(this.disabled){
13640             return;
13641         }
13642         
13643         this.page = 0;
13644         this.loadNext = false;
13645         this.hasFocus = true;
13646         
13647         if(this.triggerAction == 'all') {
13648             this.doQuery(this.allQuery, true);
13649         } else {
13650             this.doQuery(this.getRawValue());
13651         }
13652     },
13653     
13654     onSearchFieldClick : function(e)
13655     {
13656         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13657             this.onTickableFooterButtonClick(e, false, false);
13658             return;
13659         }
13660         
13661         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13662             return;
13663         }
13664         
13665         this.page = 0;
13666         this.loadNext = false;
13667         this.hasFocus = true;
13668         
13669         if(this.triggerAction == 'all') {
13670             this.doQuery(this.allQuery, true);
13671         } else {
13672             this.doQuery(this.getRawValue());
13673         }
13674     },
13675     
13676     listKeyPress : function(e)
13677     {
13678         //Roo.log('listkeypress');
13679         // scroll to first matching element based on key pres..
13680         if (e.isSpecialKey()) {
13681             return false;
13682         }
13683         var k = String.fromCharCode(e.getKey()).toUpperCase();
13684         //Roo.log(k);
13685         var match  = false;
13686         var csel = this.view.getSelectedNodes();
13687         var cselitem = false;
13688         if (csel.length) {
13689             var ix = this.view.indexOf(csel[0]);
13690             cselitem  = this.store.getAt(ix);
13691             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13692                 cselitem = false;
13693             }
13694             
13695         }
13696         
13697         this.store.each(function(v) { 
13698             if (cselitem) {
13699                 // start at existing selection.
13700                 if (cselitem.id == v.id) {
13701                     cselitem = false;
13702                 }
13703                 return true;
13704             }
13705                 
13706             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13707                 match = this.store.indexOf(v);
13708                 return false;
13709             }
13710             return true;
13711         }, this);
13712         
13713         if (match === false) {
13714             return true; // no more action?
13715         }
13716         // scroll to?
13717         this.view.select(match);
13718         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13719         sn.scrollIntoView(sn.dom.parentNode, false);
13720     },
13721     
13722     onViewScroll : function(e, t){
13723         
13724         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){
13725             return;
13726         }
13727         
13728         this.hasQuery = true;
13729         
13730         this.loading = this.list.select('.loading', true).first();
13731         
13732         if(this.loading === null){
13733             this.list.createChild({
13734                 tag: 'div',
13735                 cls: 'loading roo-select2-more-results roo-select2-active',
13736                 html: 'Loading more results...'
13737             });
13738             
13739             this.loading = this.list.select('.loading', true).first();
13740             
13741             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13742             
13743             this.loading.hide();
13744         }
13745         
13746         this.loading.show();
13747         
13748         var _combo = this;
13749         
13750         this.page++;
13751         this.loadNext = true;
13752         
13753         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13754         
13755         return;
13756     },
13757     
13758     addItem : function(o)
13759     {   
13760         var dv = ''; // display value
13761         
13762         if (this.displayField) {
13763             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13764         } else {
13765             // this is an error condition!!!
13766             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13767         }
13768         
13769         if(!dv.length){
13770             return;
13771         }
13772         
13773         var choice = this.choices.createChild({
13774             tag: 'li',
13775             cls: 'roo-select2-search-choice',
13776             cn: [
13777                 {
13778                     tag: 'div',
13779                     html: dv
13780                 },
13781                 {
13782                     tag: 'a',
13783                     href: '#',
13784                     cls: 'roo-select2-search-choice-close',
13785                     tabindex: '-1'
13786                 }
13787             ]
13788             
13789         }, this.searchField);
13790         
13791         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13792         
13793         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13794         
13795         this.item.push(o);
13796         
13797         this.lastData = o;
13798         
13799         this.syncValue();
13800         
13801         this.inputEl().dom.value = '';
13802         
13803         this.validate();
13804     },
13805     
13806     onRemoveItem : function(e, _self, o)
13807     {
13808         e.preventDefault();
13809         
13810         this.lastItem = Roo.apply([], this.item);
13811         
13812         var index = this.item.indexOf(o.data) * 1;
13813         
13814         if( index < 0){
13815             Roo.log('not this item?!');
13816             return;
13817         }
13818         
13819         this.item.splice(index, 1);
13820         o.item.remove();
13821         
13822         this.syncValue();
13823         
13824         this.fireEvent('remove', this, e);
13825         
13826         this.validate();
13827         
13828     },
13829     
13830     syncValue : function()
13831     {
13832         if(!this.item.length){
13833             this.clearValue();
13834             return;
13835         }
13836             
13837         var value = [];
13838         var _this = this;
13839         Roo.each(this.item, function(i){
13840             if(_this.valueField){
13841                 value.push(i[_this.valueField]);
13842                 return;
13843             }
13844
13845             value.push(i);
13846         });
13847
13848         this.value = value.join(',');
13849
13850         if(this.hiddenField){
13851             this.hiddenField.dom.value = this.value;
13852         }
13853         
13854         this.store.fireEvent("datachanged", this.store);
13855         
13856         this.validate();
13857     },
13858     
13859     clearItem : function()
13860     {
13861         if(!this.multiple){
13862             return;
13863         }
13864         
13865         this.item = [];
13866         
13867         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13868            c.remove();
13869         });
13870         
13871         this.syncValue();
13872         
13873         this.validate();
13874         
13875         if(this.tickable && !Roo.isTouch){
13876             this.view.refresh();
13877         }
13878     },
13879     
13880     inputEl: function ()
13881     {
13882         if(Roo.isTouch && this.mobileTouchView){
13883             return this.el.select('input.form-control',true).first();
13884         }
13885         
13886         if(this.tickable){
13887             return this.searchField;
13888         }
13889         
13890         return this.el.select('input.form-control',true).first();
13891     },
13892     
13893     
13894     onTickableFooterButtonClick : function(e, btn, el)
13895     {
13896         e.preventDefault();
13897         
13898         this.lastItem = Roo.apply([], this.item);
13899         
13900         if(btn && btn.name == 'cancel'){
13901             this.tickItems = Roo.apply([], this.item);
13902             this.collapse();
13903             return;
13904         }
13905         
13906         this.clearItem();
13907         
13908         var _this = this;
13909         
13910         Roo.each(this.tickItems, function(o){
13911             _this.addItem(o);
13912         });
13913         
13914         this.collapse();
13915         
13916     },
13917     
13918     validate : function()
13919     {
13920         var v = this.getRawValue();
13921         
13922         if(this.multiple){
13923             v = this.getValue();
13924         }
13925         
13926         if(this.disabled || this.allowBlank || v.length){
13927             this.markValid();
13928             return true;
13929         }
13930         
13931         this.markInvalid();
13932         return false;
13933     },
13934     
13935     tickableInputEl : function()
13936     {
13937         if(!this.tickable || !this.editable){
13938             return this.inputEl();
13939         }
13940         
13941         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13942     },
13943     
13944     
13945     getAutoCreateTouchView : function()
13946     {
13947         var id = Roo.id();
13948         
13949         var cfg = {
13950             cls: 'form-group' //input-group
13951         };
13952         
13953         var input =  {
13954             tag: 'input',
13955             id : id,
13956             type : this.inputType,
13957             cls : 'form-control x-combo-noedit',
13958             autocomplete: 'new-password',
13959             placeholder : this.placeholder || '',
13960             readonly : true
13961         };
13962         
13963         if (this.name) {
13964             input.name = this.name;
13965         }
13966         
13967         if (this.size) {
13968             input.cls += ' input-' + this.size;
13969         }
13970         
13971         if (this.disabled) {
13972             input.disabled = true;
13973         }
13974         
13975         var inputblock = {
13976             cls : '',
13977             cn : [
13978                 input
13979             ]
13980         };
13981         
13982         if(this.before){
13983             inputblock.cls += ' input-group';
13984             
13985             inputblock.cn.unshift({
13986                 tag :'span',
13987                 cls : 'input-group-addon',
13988                 html : this.before
13989             });
13990         }
13991         
13992         if(this.removable && !this.multiple){
13993             inputblock.cls += ' roo-removable';
13994             
13995             inputblock.cn.push({
13996                 tag: 'button',
13997                 html : 'x',
13998                 cls : 'roo-combo-removable-btn close'
13999             });
14000         }
14001
14002         if(this.hasFeedback && !this.allowBlank){
14003             
14004             inputblock.cls += ' has-feedback';
14005             
14006             inputblock.cn.push({
14007                 tag: 'span',
14008                 cls: 'glyphicon form-control-feedback'
14009             });
14010             
14011         }
14012         
14013         if (this.after) {
14014             
14015             inputblock.cls += (this.before) ? '' : ' input-group';
14016             
14017             inputblock.cn.push({
14018                 tag :'span',
14019                 cls : 'input-group-addon',
14020                 html : this.after
14021             });
14022         }
14023
14024         var box = {
14025             tag: 'div',
14026             cn: [
14027                 {
14028                     tag: 'input',
14029                     type : 'hidden',
14030                     cls: 'form-hidden-field'
14031                 },
14032                 inputblock
14033             ]
14034             
14035         };
14036         
14037         if(this.multiple){
14038             box = {
14039                 tag: 'div',
14040                 cn: [
14041                     {
14042                         tag: 'input',
14043                         type : 'hidden',
14044                         cls: 'form-hidden-field'
14045                     },
14046                     {
14047                         tag: 'ul',
14048                         cls: 'roo-select2-choices',
14049                         cn:[
14050                             {
14051                                 tag: 'li',
14052                                 cls: 'roo-select2-search-field',
14053                                 cn: [
14054
14055                                     inputblock
14056                                 ]
14057                             }
14058                         ]
14059                     }
14060                 ]
14061             }
14062         };
14063         
14064         var combobox = {
14065             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14066             cn: [
14067                 box
14068             ]
14069         };
14070         
14071         if(!this.multiple && this.showToggleBtn){
14072             
14073             var caret = {
14074                         tag: 'span',
14075                         cls: 'caret'
14076             };
14077             
14078             if (this.caret != false) {
14079                 caret = {
14080                      tag: 'i',
14081                      cls: 'fa fa-' + this.caret
14082                 };
14083                 
14084             }
14085             
14086             combobox.cn.push({
14087                 tag :'span',
14088                 cls : 'input-group-addon btn dropdown-toggle',
14089                 cn : [
14090                     caret,
14091                     {
14092                         tag: 'span',
14093                         cls: 'combobox-clear',
14094                         cn  : [
14095                             {
14096                                 tag : 'i',
14097                                 cls: 'icon-remove'
14098                             }
14099                         ]
14100                     }
14101                 ]
14102
14103             })
14104         }
14105         
14106         if(this.multiple){
14107             combobox.cls += ' roo-select2-container-multi';
14108         }
14109         
14110         var align = this.labelAlign || this.parentLabelAlign();
14111         
14112         cfg.cn = combobox;
14113         
14114         if(this.fieldLabel.length && this.labelWidth){
14115             
14116             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14117             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14118             
14119             cfg.cn = [
14120                 {
14121                    tag : 'i',
14122                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14123                    tooltip : 'This field is required'
14124                 },
14125                 {
14126                     tag: 'label',
14127                     cls : 'control-label ' + lw,
14128                     html : this.fieldLabel
14129
14130                 },
14131                 {
14132                     cls : cw, 
14133                     cn: [
14134                         combobox
14135                     ]
14136                 }
14137             ];
14138             
14139             if(this.indicatorpos == 'right'){
14140                 cfg.cn = [
14141                     {
14142                         tag: 'label',
14143                         cls : 'control-label ' + lw,
14144                         html : this.fieldLabel
14145
14146                     },
14147                     {
14148                        tag : 'i',
14149                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14150                        tooltip : 'This field is required'
14151                     },
14152                     {
14153                         cls : cw, 
14154                         cn: [
14155                             combobox
14156                         ]
14157                     }
14158                 ];
14159             }
14160         }
14161         
14162         var settings = this;
14163         
14164         ['xs','sm','md','lg'].map(function(size){
14165             if (settings[size]) {
14166                 cfg.cls += ' col-' + size + '-' + settings[size];
14167             }
14168         });
14169         
14170         return cfg;
14171     },
14172     
14173     initTouchView : function()
14174     {
14175         this.renderTouchView();
14176         
14177         this.touchViewEl.on('scroll', function(){
14178             this.el.dom.scrollTop = 0;
14179         }, this);
14180         
14181         this.originalValue = this.getValue();
14182         
14183         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14184         
14185         this.inputEl().on("click", this.showTouchView, this);
14186         this.triggerEl.on("click", this.showTouchView, this);
14187         
14188         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14189         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14190         
14191         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14192         
14193         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14194         this.store.on('load', this.onTouchViewLoad, this);
14195         this.store.on('loadexception', this.onTouchViewLoadException, this);
14196         
14197         if(this.hiddenName){
14198             
14199             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14200             
14201             this.hiddenField.dom.value =
14202                 this.hiddenValue !== undefined ? this.hiddenValue :
14203                 this.value !== undefined ? this.value : '';
14204         
14205             this.el.dom.removeAttribute('name');
14206             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14207         }
14208         
14209         if(this.multiple){
14210             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14211             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14212         }
14213         
14214         if(this.removable && !this.multiple){
14215             var close = this.closeTriggerEl();
14216             if(close){
14217                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14218                 close.on('click', this.removeBtnClick, this, close);
14219             }
14220         }
14221         /*
14222          * fix the bug in Safari iOS8
14223          */
14224         this.inputEl().on("focus", function(e){
14225             document.activeElement.blur();
14226         }, this);
14227         
14228         return;
14229         
14230         
14231     },
14232     
14233     renderTouchView : function()
14234     {
14235         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14236         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14237         
14238         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14239         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14240         
14241         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14242         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14243         this.touchViewBodyEl.setStyle('overflow', 'auto');
14244         
14245         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14246         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14247         
14248         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14249         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14250         
14251     },
14252     
14253     showTouchView : function()
14254     {
14255         if(this.disabled){
14256             return;
14257         }
14258         
14259         this.touchViewHeaderEl.hide();
14260
14261         if(this.modalTitle.length){
14262             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14263             this.touchViewHeaderEl.show();
14264         }
14265
14266         this.touchViewEl.show();
14267
14268         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14269         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14270                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14271
14272         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14273
14274         if(this.modalTitle.length){
14275             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14276         }
14277         
14278         this.touchViewBodyEl.setHeight(bodyHeight);
14279
14280         if(this.animate){
14281             var _this = this;
14282             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14283         }else{
14284             this.touchViewEl.addClass('in');
14285         }
14286
14287         this.doTouchViewQuery();
14288         
14289     },
14290     
14291     hideTouchView : function()
14292     {
14293         this.touchViewEl.removeClass('in');
14294
14295         if(this.animate){
14296             var _this = this;
14297             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14298         }else{
14299             this.touchViewEl.setStyle('display', 'none');
14300         }
14301         
14302     },
14303     
14304     setTouchViewValue : function()
14305     {
14306         if(this.multiple){
14307             this.clearItem();
14308         
14309             var _this = this;
14310
14311             Roo.each(this.tickItems, function(o){
14312                 this.addItem(o);
14313             }, this);
14314         }
14315         
14316         this.hideTouchView();
14317     },
14318     
14319     doTouchViewQuery : function()
14320     {
14321         var qe = {
14322             query: '',
14323             forceAll: true,
14324             combo: this,
14325             cancel:false
14326         };
14327         
14328         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14329             return false;
14330         }
14331         
14332         if(!this.alwaysQuery || this.mode == 'local'){
14333             this.onTouchViewLoad();
14334             return;
14335         }
14336         
14337         this.store.load();
14338     },
14339     
14340     onTouchViewBeforeLoad : function(combo,opts)
14341     {
14342         return;
14343     },
14344
14345     // private
14346     onTouchViewLoad : function()
14347     {
14348         if(this.store.getCount() < 1){
14349             this.onTouchViewEmptyResults();
14350             return;
14351         }
14352         
14353         this.clearTouchView();
14354         
14355         var rawValue = this.getRawValue();
14356         
14357         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14358         
14359         this.tickItems = [];
14360         
14361         this.store.data.each(function(d, rowIndex){
14362             var row = this.touchViewListGroup.createChild(template);
14363             
14364             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14365                 row.addClass(d.data.cls);
14366             }
14367             
14368             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14369                 var cfg = {
14370                     data : d.data,
14371                     html : d.data[this.displayField]
14372                 };
14373                 
14374                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14375                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14376                 }
14377             }
14378             row.removeClass('selected');
14379             if(!this.multiple && this.valueField &&
14380                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14381             {
14382                 // radio buttons..
14383                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14384                 row.addClass('selected');
14385             }
14386             
14387             if(this.multiple && this.valueField &&
14388                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14389             {
14390                 
14391                 // checkboxes...
14392                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14393                 this.tickItems.push(d.data);
14394             }
14395             
14396             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14397             
14398         }, this);
14399         
14400         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14401         
14402         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14403
14404         if(this.modalTitle.length){
14405             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14406         }
14407
14408         var listHeight = this.touchViewListGroup.getHeight();
14409         
14410         var _this = this;
14411         
14412         if(firstChecked && listHeight > bodyHeight){
14413             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14414         }
14415         
14416     },
14417     
14418     onTouchViewLoadException : function()
14419     {
14420         this.hideTouchView();
14421     },
14422     
14423     onTouchViewEmptyResults : function()
14424     {
14425         this.clearTouchView();
14426         
14427         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14428         
14429         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14430         
14431     },
14432     
14433     clearTouchView : function()
14434     {
14435         this.touchViewListGroup.dom.innerHTML = '';
14436     },
14437     
14438     onTouchViewClick : function(e, el, o)
14439     {
14440         e.preventDefault();
14441         
14442         var row = o.row;
14443         var rowIndex = o.rowIndex;
14444         
14445         var r = this.store.getAt(rowIndex);
14446         
14447         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14448             
14449             if(!this.multiple){
14450                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14451                     c.dom.removeAttribute('checked');
14452                 }, this);
14453
14454                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14455
14456                 this.setFromData(r.data);
14457
14458                 var close = this.closeTriggerEl();
14459
14460                 if(close){
14461                     close.show();
14462                 }
14463
14464                 this.hideTouchView();
14465
14466                 this.fireEvent('select', this, r, rowIndex);
14467
14468                 return;
14469             }
14470
14471             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14472                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14473                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14474                 return;
14475             }
14476
14477             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14478             this.addItem(r.data);
14479             this.tickItems.push(r.data);
14480         }
14481     }
14482     
14483
14484     /** 
14485     * @cfg {Boolean} grow 
14486     * @hide 
14487     */
14488     /** 
14489     * @cfg {Number} growMin 
14490     * @hide 
14491     */
14492     /** 
14493     * @cfg {Number} growMax 
14494     * @hide 
14495     */
14496     /**
14497      * @hide
14498      * @method autoSize
14499      */
14500 });
14501
14502 Roo.apply(Roo.bootstrap.ComboBox,  {
14503     
14504     header : {
14505         tag: 'div',
14506         cls: 'modal-header',
14507         cn: [
14508             {
14509                 tag: 'h4',
14510                 cls: 'modal-title'
14511             }
14512         ]
14513     },
14514     
14515     body : {
14516         tag: 'div',
14517         cls: 'modal-body',
14518         cn: [
14519             {
14520                 tag: 'ul',
14521                 cls: 'list-group'
14522             }
14523         ]
14524     },
14525     
14526     listItemRadio : {
14527         tag: 'li',
14528         cls: 'list-group-item',
14529         cn: [
14530             {
14531                 tag: 'span',
14532                 cls: 'roo-combobox-list-group-item-value'
14533             },
14534             {
14535                 tag: 'div',
14536                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14537                 cn: [
14538                     {
14539                         tag: 'input',
14540                         type: 'radio'
14541                     },
14542                     {
14543                         tag: 'label'
14544                     }
14545                 ]
14546             }
14547         ]
14548     },
14549     
14550     listItemCheckbox : {
14551         tag: 'li',
14552         cls: 'list-group-item',
14553         cn: [
14554             {
14555                 tag: 'span',
14556                 cls: 'roo-combobox-list-group-item-value'
14557             },
14558             {
14559                 tag: 'div',
14560                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14561                 cn: [
14562                     {
14563                         tag: 'input',
14564                         type: 'checkbox'
14565                     },
14566                     {
14567                         tag: 'label'
14568                     }
14569                 ]
14570             }
14571         ]
14572     },
14573     
14574     emptyResult : {
14575         tag: 'div',
14576         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14577     },
14578     
14579     footer : {
14580         tag: 'div',
14581         cls: 'modal-footer',
14582         cn: [
14583             {
14584                 tag: 'div',
14585                 cls: 'row',
14586                 cn: [
14587                     {
14588                         tag: 'div',
14589                         cls: 'col-xs-6 text-left',
14590                         cn: {
14591                             tag: 'button',
14592                             cls: 'btn btn-danger roo-touch-view-cancel',
14593                             html: 'Cancel'
14594                         }
14595                     },
14596                     {
14597                         tag: 'div',
14598                         cls: 'col-xs-6 text-right',
14599                         cn: {
14600                             tag: 'button',
14601                             cls: 'btn btn-success roo-touch-view-ok',
14602                             html: 'OK'
14603                         }
14604                     }
14605                 ]
14606             }
14607         ]
14608         
14609     }
14610 });
14611
14612 Roo.apply(Roo.bootstrap.ComboBox,  {
14613     
14614     touchViewTemplate : {
14615         tag: 'div',
14616         cls: 'modal fade roo-combobox-touch-view',
14617         cn: [
14618             {
14619                 tag: 'div',
14620                 cls: 'modal-dialog',
14621                 style : 'position:fixed', // we have to fix position....
14622                 cn: [
14623                     {
14624                         tag: 'div',
14625                         cls: 'modal-content',
14626                         cn: [
14627                             Roo.bootstrap.ComboBox.header,
14628                             Roo.bootstrap.ComboBox.body,
14629                             Roo.bootstrap.ComboBox.footer
14630                         ]
14631                     }
14632                 ]
14633             }
14634         ]
14635     }
14636 });/*
14637  * Based on:
14638  * Ext JS Library 1.1.1
14639  * Copyright(c) 2006-2007, Ext JS, LLC.
14640  *
14641  * Originally Released Under LGPL - original licence link has changed is not relivant.
14642  *
14643  * Fork - LGPL
14644  * <script type="text/javascript">
14645  */
14646
14647 /**
14648  * @class Roo.View
14649  * @extends Roo.util.Observable
14650  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14651  * This class also supports single and multi selection modes. <br>
14652  * Create a data model bound view:
14653  <pre><code>
14654  var store = new Roo.data.Store(...);
14655
14656  var view = new Roo.View({
14657     el : "my-element",
14658     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14659  
14660     singleSelect: true,
14661     selectedClass: "ydataview-selected",
14662     store: store
14663  });
14664
14665  // listen for node click?
14666  view.on("click", function(vw, index, node, e){
14667  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14668  });
14669
14670  // load XML data
14671  dataModel.load("foobar.xml");
14672  </code></pre>
14673  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14674  * <br><br>
14675  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14676  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14677  * 
14678  * Note: old style constructor is still suported (container, template, config)
14679  * 
14680  * @constructor
14681  * Create a new View
14682  * @param {Object} config The config object
14683  * 
14684  */
14685 Roo.View = function(config, depreciated_tpl, depreciated_config){
14686     
14687     this.parent = false;
14688     
14689     if (typeof(depreciated_tpl) == 'undefined') {
14690         // new way.. - universal constructor.
14691         Roo.apply(this, config);
14692         this.el  = Roo.get(this.el);
14693     } else {
14694         // old format..
14695         this.el  = Roo.get(config);
14696         this.tpl = depreciated_tpl;
14697         Roo.apply(this, depreciated_config);
14698     }
14699     this.wrapEl  = this.el.wrap().wrap();
14700     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14701     
14702     
14703     if(typeof(this.tpl) == "string"){
14704         this.tpl = new Roo.Template(this.tpl);
14705     } else {
14706         // support xtype ctors..
14707         this.tpl = new Roo.factory(this.tpl, Roo);
14708     }
14709     
14710     
14711     this.tpl.compile();
14712     
14713     /** @private */
14714     this.addEvents({
14715         /**
14716          * @event beforeclick
14717          * Fires before a click is processed. Returns false to cancel the default action.
14718          * @param {Roo.View} this
14719          * @param {Number} index The index of the target node
14720          * @param {HTMLElement} node The target node
14721          * @param {Roo.EventObject} e The raw event object
14722          */
14723             "beforeclick" : true,
14724         /**
14725          * @event click
14726          * Fires when a template node is clicked.
14727          * @param {Roo.View} this
14728          * @param {Number} index The index of the target node
14729          * @param {HTMLElement} node The target node
14730          * @param {Roo.EventObject} e The raw event object
14731          */
14732             "click" : true,
14733         /**
14734          * @event dblclick
14735          * Fires when a template node is double clicked.
14736          * @param {Roo.View} this
14737          * @param {Number} index The index of the target node
14738          * @param {HTMLElement} node The target node
14739          * @param {Roo.EventObject} e The raw event object
14740          */
14741             "dblclick" : true,
14742         /**
14743          * @event contextmenu
14744          * Fires when a template node is right clicked.
14745          * @param {Roo.View} this
14746          * @param {Number} index The index of the target node
14747          * @param {HTMLElement} node The target node
14748          * @param {Roo.EventObject} e The raw event object
14749          */
14750             "contextmenu" : true,
14751         /**
14752          * @event selectionchange
14753          * Fires when the selected nodes change.
14754          * @param {Roo.View} this
14755          * @param {Array} selections Array of the selected nodes
14756          */
14757             "selectionchange" : true,
14758     
14759         /**
14760          * @event beforeselect
14761          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14762          * @param {Roo.View} this
14763          * @param {HTMLElement} node The node to be selected
14764          * @param {Array} selections Array of currently selected nodes
14765          */
14766             "beforeselect" : true,
14767         /**
14768          * @event preparedata
14769          * Fires on every row to render, to allow you to change the data.
14770          * @param {Roo.View} this
14771          * @param {Object} data to be rendered (change this)
14772          */
14773           "preparedata" : true
14774           
14775           
14776         });
14777
14778
14779
14780     this.el.on({
14781         "click": this.onClick,
14782         "dblclick": this.onDblClick,
14783         "contextmenu": this.onContextMenu,
14784         scope:this
14785     });
14786
14787     this.selections = [];
14788     this.nodes = [];
14789     this.cmp = new Roo.CompositeElementLite([]);
14790     if(this.store){
14791         this.store = Roo.factory(this.store, Roo.data);
14792         this.setStore(this.store, true);
14793     }
14794     
14795     if ( this.footer && this.footer.xtype) {
14796            
14797          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14798         
14799         this.footer.dataSource = this.store;
14800         this.footer.container = fctr;
14801         this.footer = Roo.factory(this.footer, Roo);
14802         fctr.insertFirst(this.el);
14803         
14804         // this is a bit insane - as the paging toolbar seems to detach the el..
14805 //        dom.parentNode.parentNode.parentNode
14806          // they get detached?
14807     }
14808     
14809     
14810     Roo.View.superclass.constructor.call(this);
14811     
14812     
14813 };
14814
14815 Roo.extend(Roo.View, Roo.util.Observable, {
14816     
14817      /**
14818      * @cfg {Roo.data.Store} store Data store to load data from.
14819      */
14820     store : false,
14821     
14822     /**
14823      * @cfg {String|Roo.Element} el The container element.
14824      */
14825     el : '',
14826     
14827     /**
14828      * @cfg {String|Roo.Template} tpl The template used by this View 
14829      */
14830     tpl : false,
14831     /**
14832      * @cfg {String} dataName the named area of the template to use as the data area
14833      *                          Works with domtemplates roo-name="name"
14834      */
14835     dataName: false,
14836     /**
14837      * @cfg {String} selectedClass The css class to add to selected nodes
14838      */
14839     selectedClass : "x-view-selected",
14840      /**
14841      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14842      */
14843     emptyText : "",
14844     
14845     /**
14846      * @cfg {String} text to display on mask (default Loading)
14847      */
14848     mask : false,
14849     /**
14850      * @cfg {Boolean} multiSelect Allow multiple selection
14851      */
14852     multiSelect : false,
14853     /**
14854      * @cfg {Boolean} singleSelect Allow single selection
14855      */
14856     singleSelect:  false,
14857     
14858     /**
14859      * @cfg {Boolean} toggleSelect - selecting 
14860      */
14861     toggleSelect : false,
14862     
14863     /**
14864      * @cfg {Boolean} tickable - selecting 
14865      */
14866     tickable : false,
14867     
14868     /**
14869      * Returns the element this view is bound to.
14870      * @return {Roo.Element}
14871      */
14872     getEl : function(){
14873         return this.wrapEl;
14874     },
14875     
14876     
14877
14878     /**
14879      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14880      */
14881     refresh : function(){
14882         //Roo.log('refresh');
14883         var t = this.tpl;
14884         
14885         // if we are using something like 'domtemplate', then
14886         // the what gets used is:
14887         // t.applySubtemplate(NAME, data, wrapping data..)
14888         // the outer template then get' applied with
14889         //     the store 'extra data'
14890         // and the body get's added to the
14891         //      roo-name="data" node?
14892         //      <span class='roo-tpl-{name}'></span> ?????
14893         
14894         
14895         
14896         this.clearSelections();
14897         this.el.update("");
14898         var html = [];
14899         var records = this.store.getRange();
14900         if(records.length < 1) {
14901             
14902             // is this valid??  = should it render a template??
14903             
14904             this.el.update(this.emptyText);
14905             return;
14906         }
14907         var el = this.el;
14908         if (this.dataName) {
14909             this.el.update(t.apply(this.store.meta)); //????
14910             el = this.el.child('.roo-tpl-' + this.dataName);
14911         }
14912         
14913         for(var i = 0, len = records.length; i < len; i++){
14914             var data = this.prepareData(records[i].data, i, records[i]);
14915             this.fireEvent("preparedata", this, data, i, records[i]);
14916             
14917             var d = Roo.apply({}, data);
14918             
14919             if(this.tickable){
14920                 Roo.apply(d, {'roo-id' : Roo.id()});
14921                 
14922                 var _this = this;
14923             
14924                 Roo.each(this.parent.item, function(item){
14925                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14926                         return;
14927                     }
14928                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14929                 });
14930             }
14931             
14932             html[html.length] = Roo.util.Format.trim(
14933                 this.dataName ?
14934                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14935                     t.apply(d)
14936             );
14937         }
14938         
14939         
14940         
14941         el.update(html.join(""));
14942         this.nodes = el.dom.childNodes;
14943         this.updateIndexes(0);
14944     },
14945     
14946
14947     /**
14948      * Function to override to reformat the data that is sent to
14949      * the template for each node.
14950      * DEPRICATED - use the preparedata event handler.
14951      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14952      * a JSON object for an UpdateManager bound view).
14953      */
14954     prepareData : function(data, index, record)
14955     {
14956         this.fireEvent("preparedata", this, data, index, record);
14957         return data;
14958     },
14959
14960     onUpdate : function(ds, record){
14961         // Roo.log('on update');   
14962         this.clearSelections();
14963         var index = this.store.indexOf(record);
14964         var n = this.nodes[index];
14965         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14966         n.parentNode.removeChild(n);
14967         this.updateIndexes(index, index);
14968     },
14969
14970     
14971     
14972 // --------- FIXME     
14973     onAdd : function(ds, records, index)
14974     {
14975         //Roo.log(['on Add', ds, records, index] );        
14976         this.clearSelections();
14977         if(this.nodes.length == 0){
14978             this.refresh();
14979             return;
14980         }
14981         var n = this.nodes[index];
14982         for(var i = 0, len = records.length; i < len; i++){
14983             var d = this.prepareData(records[i].data, i, records[i]);
14984             if(n){
14985                 this.tpl.insertBefore(n, d);
14986             }else{
14987                 
14988                 this.tpl.append(this.el, d);
14989             }
14990         }
14991         this.updateIndexes(index);
14992     },
14993
14994     onRemove : function(ds, record, index){
14995        // Roo.log('onRemove');
14996         this.clearSelections();
14997         var el = this.dataName  ?
14998             this.el.child('.roo-tpl-' + this.dataName) :
14999             this.el; 
15000         
15001         el.dom.removeChild(this.nodes[index]);
15002         this.updateIndexes(index);
15003     },
15004
15005     /**
15006      * Refresh an individual node.
15007      * @param {Number} index
15008      */
15009     refreshNode : function(index){
15010         this.onUpdate(this.store, this.store.getAt(index));
15011     },
15012
15013     updateIndexes : function(startIndex, endIndex){
15014         var ns = this.nodes;
15015         startIndex = startIndex || 0;
15016         endIndex = endIndex || ns.length - 1;
15017         for(var i = startIndex; i <= endIndex; i++){
15018             ns[i].nodeIndex = i;
15019         }
15020     },
15021
15022     /**
15023      * Changes the data store this view uses and refresh the view.
15024      * @param {Store} store
15025      */
15026     setStore : function(store, initial){
15027         if(!initial && this.store){
15028             this.store.un("datachanged", this.refresh);
15029             this.store.un("add", this.onAdd);
15030             this.store.un("remove", this.onRemove);
15031             this.store.un("update", this.onUpdate);
15032             this.store.un("clear", this.refresh);
15033             this.store.un("beforeload", this.onBeforeLoad);
15034             this.store.un("load", this.onLoad);
15035             this.store.un("loadexception", this.onLoad);
15036         }
15037         if(store){
15038           
15039             store.on("datachanged", this.refresh, this);
15040             store.on("add", this.onAdd, this);
15041             store.on("remove", this.onRemove, this);
15042             store.on("update", this.onUpdate, this);
15043             store.on("clear", this.refresh, this);
15044             store.on("beforeload", this.onBeforeLoad, this);
15045             store.on("load", this.onLoad, this);
15046             store.on("loadexception", this.onLoad, this);
15047         }
15048         
15049         if(store){
15050             this.refresh();
15051         }
15052     },
15053     /**
15054      * onbeforeLoad - masks the loading area.
15055      *
15056      */
15057     onBeforeLoad : function(store,opts)
15058     {
15059          //Roo.log('onBeforeLoad');   
15060         if (!opts.add) {
15061             this.el.update("");
15062         }
15063         this.el.mask(this.mask ? this.mask : "Loading" ); 
15064     },
15065     onLoad : function ()
15066     {
15067         this.el.unmask();
15068     },
15069     
15070
15071     /**
15072      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15073      * @param {HTMLElement} node
15074      * @return {HTMLElement} The template node
15075      */
15076     findItemFromChild : function(node){
15077         var el = this.dataName  ?
15078             this.el.child('.roo-tpl-' + this.dataName,true) :
15079             this.el.dom; 
15080         
15081         if(!node || node.parentNode == el){
15082                     return node;
15083             }
15084             var p = node.parentNode;
15085             while(p && p != el){
15086             if(p.parentNode == el){
15087                 return p;
15088             }
15089             p = p.parentNode;
15090         }
15091             return null;
15092     },
15093
15094     /** @ignore */
15095     onClick : function(e){
15096         var item = this.findItemFromChild(e.getTarget());
15097         if(item){
15098             var index = this.indexOf(item);
15099             if(this.onItemClick(item, index, e) !== false){
15100                 this.fireEvent("click", this, index, item, e);
15101             }
15102         }else{
15103             this.clearSelections();
15104         }
15105     },
15106
15107     /** @ignore */
15108     onContextMenu : function(e){
15109         var item = this.findItemFromChild(e.getTarget());
15110         if(item){
15111             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15112         }
15113     },
15114
15115     /** @ignore */
15116     onDblClick : function(e){
15117         var item = this.findItemFromChild(e.getTarget());
15118         if(item){
15119             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15120         }
15121     },
15122
15123     onItemClick : function(item, index, e)
15124     {
15125         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15126             return false;
15127         }
15128         if (this.toggleSelect) {
15129             var m = this.isSelected(item) ? 'unselect' : 'select';
15130             //Roo.log(m);
15131             var _t = this;
15132             _t[m](item, true, false);
15133             return true;
15134         }
15135         if(this.multiSelect || this.singleSelect){
15136             if(this.multiSelect && e.shiftKey && this.lastSelection){
15137                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15138             }else{
15139                 this.select(item, this.multiSelect && e.ctrlKey);
15140                 this.lastSelection = item;
15141             }
15142             
15143             if(!this.tickable){
15144                 e.preventDefault();
15145             }
15146             
15147         }
15148         return true;
15149     },
15150
15151     /**
15152      * Get the number of selected nodes.
15153      * @return {Number}
15154      */
15155     getSelectionCount : function(){
15156         return this.selections.length;
15157     },
15158
15159     /**
15160      * Get the currently selected nodes.
15161      * @return {Array} An array of HTMLElements
15162      */
15163     getSelectedNodes : function(){
15164         return this.selections;
15165     },
15166
15167     /**
15168      * Get the indexes of the selected nodes.
15169      * @return {Array}
15170      */
15171     getSelectedIndexes : function(){
15172         var indexes = [], s = this.selections;
15173         for(var i = 0, len = s.length; i < len; i++){
15174             indexes.push(s[i].nodeIndex);
15175         }
15176         return indexes;
15177     },
15178
15179     /**
15180      * Clear all selections
15181      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15182      */
15183     clearSelections : function(suppressEvent){
15184         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15185             this.cmp.elements = this.selections;
15186             this.cmp.removeClass(this.selectedClass);
15187             this.selections = [];
15188             if(!suppressEvent){
15189                 this.fireEvent("selectionchange", this, this.selections);
15190             }
15191         }
15192     },
15193
15194     /**
15195      * Returns true if the passed node is selected
15196      * @param {HTMLElement/Number} node The node or node index
15197      * @return {Boolean}
15198      */
15199     isSelected : function(node){
15200         var s = this.selections;
15201         if(s.length < 1){
15202             return false;
15203         }
15204         node = this.getNode(node);
15205         return s.indexOf(node) !== -1;
15206     },
15207
15208     /**
15209      * Selects nodes.
15210      * @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
15211      * @param {Boolean} keepExisting (optional) true to keep existing selections
15212      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15213      */
15214     select : function(nodeInfo, keepExisting, suppressEvent){
15215         if(nodeInfo instanceof Array){
15216             if(!keepExisting){
15217                 this.clearSelections(true);
15218             }
15219             for(var i = 0, len = nodeInfo.length; i < len; i++){
15220                 this.select(nodeInfo[i], true, true);
15221             }
15222             return;
15223         } 
15224         var node = this.getNode(nodeInfo);
15225         if(!node || this.isSelected(node)){
15226             return; // already selected.
15227         }
15228         if(!keepExisting){
15229             this.clearSelections(true);
15230         }
15231         
15232         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15233             Roo.fly(node).addClass(this.selectedClass);
15234             this.selections.push(node);
15235             if(!suppressEvent){
15236                 this.fireEvent("selectionchange", this, this.selections);
15237             }
15238         }
15239         
15240         
15241     },
15242       /**
15243      * Unselects nodes.
15244      * @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
15245      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15246      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15247      */
15248     unselect : function(nodeInfo, keepExisting, suppressEvent)
15249     {
15250         if(nodeInfo instanceof Array){
15251             Roo.each(this.selections, function(s) {
15252                 this.unselect(s, nodeInfo);
15253             }, this);
15254             return;
15255         }
15256         var node = this.getNode(nodeInfo);
15257         if(!node || !this.isSelected(node)){
15258             //Roo.log("not selected");
15259             return; // not selected.
15260         }
15261         // fireevent???
15262         var ns = [];
15263         Roo.each(this.selections, function(s) {
15264             if (s == node ) {
15265                 Roo.fly(node).removeClass(this.selectedClass);
15266
15267                 return;
15268             }
15269             ns.push(s);
15270         },this);
15271         
15272         this.selections= ns;
15273         this.fireEvent("selectionchange", this, this.selections);
15274     },
15275
15276     /**
15277      * Gets a template node.
15278      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15279      * @return {HTMLElement} The node or null if it wasn't found
15280      */
15281     getNode : function(nodeInfo){
15282         if(typeof nodeInfo == "string"){
15283             return document.getElementById(nodeInfo);
15284         }else if(typeof nodeInfo == "number"){
15285             return this.nodes[nodeInfo];
15286         }
15287         return nodeInfo;
15288     },
15289
15290     /**
15291      * Gets a range template nodes.
15292      * @param {Number} startIndex
15293      * @param {Number} endIndex
15294      * @return {Array} An array of nodes
15295      */
15296     getNodes : function(start, end){
15297         var ns = this.nodes;
15298         start = start || 0;
15299         end = typeof end == "undefined" ? ns.length - 1 : end;
15300         var nodes = [];
15301         if(start <= end){
15302             for(var i = start; i <= end; i++){
15303                 nodes.push(ns[i]);
15304             }
15305         } else{
15306             for(var i = start; i >= end; i--){
15307                 nodes.push(ns[i]);
15308             }
15309         }
15310         return nodes;
15311     },
15312
15313     /**
15314      * Finds the index of the passed node
15315      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15316      * @return {Number} The index of the node or -1
15317      */
15318     indexOf : function(node){
15319         node = this.getNode(node);
15320         if(typeof node.nodeIndex == "number"){
15321             return node.nodeIndex;
15322         }
15323         var ns = this.nodes;
15324         for(var i = 0, len = ns.length; i < len; i++){
15325             if(ns[i] == node){
15326                 return i;
15327             }
15328         }
15329         return -1;
15330     }
15331 });
15332 /*
15333  * - LGPL
15334  *
15335  * based on jquery fullcalendar
15336  * 
15337  */
15338
15339 Roo.bootstrap = Roo.bootstrap || {};
15340 /**
15341  * @class Roo.bootstrap.Calendar
15342  * @extends Roo.bootstrap.Component
15343  * Bootstrap Calendar class
15344  * @cfg {Boolean} loadMask (true|false) default false
15345  * @cfg {Object} header generate the user specific header of the calendar, default false
15346
15347  * @constructor
15348  * Create a new Container
15349  * @param {Object} config The config object
15350  */
15351
15352
15353
15354 Roo.bootstrap.Calendar = function(config){
15355     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15356      this.addEvents({
15357         /**
15358              * @event select
15359              * Fires when a date is selected
15360              * @param {DatePicker} this
15361              * @param {Date} date The selected date
15362              */
15363         'select': true,
15364         /**
15365              * @event monthchange
15366              * Fires when the displayed month changes 
15367              * @param {DatePicker} this
15368              * @param {Date} date The selected month
15369              */
15370         'monthchange': true,
15371         /**
15372              * @event evententer
15373              * Fires when mouse over an event
15374              * @param {Calendar} this
15375              * @param {event} Event
15376              */
15377         'evententer': true,
15378         /**
15379              * @event eventleave
15380              * Fires when the mouse leaves an
15381              * @param {Calendar} this
15382              * @param {event}
15383              */
15384         'eventleave': true,
15385         /**
15386              * @event eventclick
15387              * Fires when the mouse click an
15388              * @param {Calendar} this
15389              * @param {event}
15390              */
15391         'eventclick': true
15392         
15393     });
15394
15395 };
15396
15397 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15398     
15399      /**
15400      * @cfg {Number} startDay
15401      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15402      */
15403     startDay : 0,
15404     
15405     loadMask : false,
15406     
15407     header : false,
15408       
15409     getAutoCreate : function(){
15410         
15411         
15412         var fc_button = function(name, corner, style, content ) {
15413             return Roo.apply({},{
15414                 tag : 'span',
15415                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15416                          (corner.length ?
15417                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15418                             ''
15419                         ),
15420                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15421                 unselectable: 'on'
15422             });
15423         };
15424         
15425         var header = {};
15426         
15427         if(!this.header){
15428             header = {
15429                 tag : 'table',
15430                 cls : 'fc-header',
15431                 style : 'width:100%',
15432                 cn : [
15433                     {
15434                         tag: 'tr',
15435                         cn : [
15436                             {
15437                                 tag : 'td',
15438                                 cls : 'fc-header-left',
15439                                 cn : [
15440                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15441                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15442                                     { tag: 'span', cls: 'fc-header-space' },
15443                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15444
15445
15446                                 ]
15447                             },
15448
15449                             {
15450                                 tag : 'td',
15451                                 cls : 'fc-header-center',
15452                                 cn : [
15453                                     {
15454                                         tag: 'span',
15455                                         cls: 'fc-header-title',
15456                                         cn : {
15457                                             tag: 'H2',
15458                                             html : 'month / year'
15459                                         }
15460                                     }
15461
15462                                 ]
15463                             },
15464                             {
15465                                 tag : 'td',
15466                                 cls : 'fc-header-right',
15467                                 cn : [
15468                               /*      fc_button('month', 'left', '', 'month' ),
15469                                     fc_button('week', '', '', 'week' ),
15470                                     fc_button('day', 'right', '', 'day' )
15471                                 */    
15472
15473                                 ]
15474                             }
15475
15476                         ]
15477                     }
15478                 ]
15479             };
15480         }
15481         
15482         header = this.header;
15483         
15484        
15485         var cal_heads = function() {
15486             var ret = [];
15487             // fixme - handle this.
15488             
15489             for (var i =0; i < Date.dayNames.length; i++) {
15490                 var d = Date.dayNames[i];
15491                 ret.push({
15492                     tag: 'th',
15493                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15494                     html : d.substring(0,3)
15495                 });
15496                 
15497             }
15498             ret[0].cls += ' fc-first';
15499             ret[6].cls += ' fc-last';
15500             return ret;
15501         };
15502         var cal_cell = function(n) {
15503             return  {
15504                 tag: 'td',
15505                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15506                 cn : [
15507                     {
15508                         cn : [
15509                             {
15510                                 cls: 'fc-day-number',
15511                                 html: 'D'
15512                             },
15513                             {
15514                                 cls: 'fc-day-content',
15515                              
15516                                 cn : [
15517                                      {
15518                                         style: 'position: relative;' // height: 17px;
15519                                     }
15520                                 ]
15521                             }
15522                             
15523                             
15524                         ]
15525                     }
15526                 ]
15527                 
15528             }
15529         };
15530         var cal_rows = function() {
15531             
15532             var ret = [];
15533             for (var r = 0; r < 6; r++) {
15534                 var row= {
15535                     tag : 'tr',
15536                     cls : 'fc-week',
15537                     cn : []
15538                 };
15539                 
15540                 for (var i =0; i < Date.dayNames.length; i++) {
15541                     var d = Date.dayNames[i];
15542                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15543
15544                 }
15545                 row.cn[0].cls+=' fc-first';
15546                 row.cn[0].cn[0].style = 'min-height:90px';
15547                 row.cn[6].cls+=' fc-last';
15548                 ret.push(row);
15549                 
15550             }
15551             ret[0].cls += ' fc-first';
15552             ret[4].cls += ' fc-prev-last';
15553             ret[5].cls += ' fc-last';
15554             return ret;
15555             
15556         };
15557         
15558         var cal_table = {
15559             tag: 'table',
15560             cls: 'fc-border-separate',
15561             style : 'width:100%',
15562             cellspacing  : 0,
15563             cn : [
15564                 { 
15565                     tag: 'thead',
15566                     cn : [
15567                         { 
15568                             tag: 'tr',
15569                             cls : 'fc-first fc-last',
15570                             cn : cal_heads()
15571                         }
15572                     ]
15573                 },
15574                 { 
15575                     tag: 'tbody',
15576                     cn : cal_rows()
15577                 }
15578                   
15579             ]
15580         };
15581          
15582          var cfg = {
15583             cls : 'fc fc-ltr',
15584             cn : [
15585                 header,
15586                 {
15587                     cls : 'fc-content',
15588                     style : "position: relative;",
15589                     cn : [
15590                         {
15591                             cls : 'fc-view fc-view-month fc-grid',
15592                             style : 'position: relative',
15593                             unselectable : 'on',
15594                             cn : [
15595                                 {
15596                                     cls : 'fc-event-container',
15597                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15598                                 },
15599                                 cal_table
15600                             ]
15601                         }
15602                     ]
15603     
15604                 }
15605            ] 
15606             
15607         };
15608         
15609          
15610         
15611         return cfg;
15612     },
15613     
15614     
15615     initEvents : function()
15616     {
15617         if(!this.store){
15618             throw "can not find store for calendar";
15619         }
15620         
15621         var mark = {
15622             tag: "div",
15623             cls:"x-dlg-mask",
15624             style: "text-align:center",
15625             cn: [
15626                 {
15627                     tag: "div",
15628                     style: "background-color:white;width:50%;margin:250 auto",
15629                     cn: [
15630                         {
15631                             tag: "img",
15632                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15633                         },
15634                         {
15635                             tag: "span",
15636                             html: "Loading"
15637                         }
15638                         
15639                     ]
15640                 }
15641             ]
15642         };
15643         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15644         
15645         var size = this.el.select('.fc-content', true).first().getSize();
15646         this.maskEl.setSize(size.width, size.height);
15647         this.maskEl.enableDisplayMode("block");
15648         if(!this.loadMask){
15649             this.maskEl.hide();
15650         }
15651         
15652         this.store = Roo.factory(this.store, Roo.data);
15653         this.store.on('load', this.onLoad, this);
15654         this.store.on('beforeload', this.onBeforeLoad, this);
15655         
15656         this.resize();
15657         
15658         this.cells = this.el.select('.fc-day',true);
15659         //Roo.log(this.cells);
15660         this.textNodes = this.el.query('.fc-day-number');
15661         this.cells.addClassOnOver('fc-state-hover');
15662         
15663         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15664         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15665         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15666         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15667         
15668         this.on('monthchange', this.onMonthChange, this);
15669         
15670         this.update(new Date().clearTime());
15671     },
15672     
15673     resize : function() {
15674         var sz  = this.el.getSize();
15675         
15676         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15677         this.el.select('.fc-day-content div',true).setHeight(34);
15678     },
15679     
15680     
15681     // private
15682     showPrevMonth : function(e){
15683         this.update(this.activeDate.add("mo", -1));
15684     },
15685     showToday : function(e){
15686         this.update(new Date().clearTime());
15687     },
15688     // private
15689     showNextMonth : function(e){
15690         this.update(this.activeDate.add("mo", 1));
15691     },
15692
15693     // private
15694     showPrevYear : function(){
15695         this.update(this.activeDate.add("y", -1));
15696     },
15697
15698     // private
15699     showNextYear : function(){
15700         this.update(this.activeDate.add("y", 1));
15701     },
15702
15703     
15704    // private
15705     update : function(date)
15706     {
15707         var vd = this.activeDate;
15708         this.activeDate = date;
15709 //        if(vd && this.el){
15710 //            var t = date.getTime();
15711 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15712 //                Roo.log('using add remove');
15713 //                
15714 //                this.fireEvent('monthchange', this, date);
15715 //                
15716 //                this.cells.removeClass("fc-state-highlight");
15717 //                this.cells.each(function(c){
15718 //                   if(c.dateValue == t){
15719 //                       c.addClass("fc-state-highlight");
15720 //                       setTimeout(function(){
15721 //                            try{c.dom.firstChild.focus();}catch(e){}
15722 //                       }, 50);
15723 //                       return false;
15724 //                   }
15725 //                   return true;
15726 //                });
15727 //                return;
15728 //            }
15729 //        }
15730         
15731         var days = date.getDaysInMonth();
15732         
15733         var firstOfMonth = date.getFirstDateOfMonth();
15734         var startingPos = firstOfMonth.getDay()-this.startDay;
15735         
15736         if(startingPos < this.startDay){
15737             startingPos += 7;
15738         }
15739         
15740         var pm = date.add(Date.MONTH, -1);
15741         var prevStart = pm.getDaysInMonth()-startingPos;
15742 //        
15743         this.cells = this.el.select('.fc-day',true);
15744         this.textNodes = this.el.query('.fc-day-number');
15745         this.cells.addClassOnOver('fc-state-hover');
15746         
15747         var cells = this.cells.elements;
15748         var textEls = this.textNodes;
15749         
15750         Roo.each(cells, function(cell){
15751             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15752         });
15753         
15754         days += startingPos;
15755
15756         // convert everything to numbers so it's fast
15757         var day = 86400000;
15758         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15759         //Roo.log(d);
15760         //Roo.log(pm);
15761         //Roo.log(prevStart);
15762         
15763         var today = new Date().clearTime().getTime();
15764         var sel = date.clearTime().getTime();
15765         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15766         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15767         var ddMatch = this.disabledDatesRE;
15768         var ddText = this.disabledDatesText;
15769         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15770         var ddaysText = this.disabledDaysText;
15771         var format = this.format;
15772         
15773         var setCellClass = function(cal, cell){
15774             cell.row = 0;
15775             cell.events = [];
15776             cell.more = [];
15777             //Roo.log('set Cell Class');
15778             cell.title = "";
15779             var t = d.getTime();
15780             
15781             //Roo.log(d);
15782             
15783             cell.dateValue = t;
15784             if(t == today){
15785                 cell.className += " fc-today";
15786                 cell.className += " fc-state-highlight";
15787                 cell.title = cal.todayText;
15788             }
15789             if(t == sel){
15790                 // disable highlight in other month..
15791                 //cell.className += " fc-state-highlight";
15792                 
15793             }
15794             // disabling
15795             if(t < min) {
15796                 cell.className = " fc-state-disabled";
15797                 cell.title = cal.minText;
15798                 return;
15799             }
15800             if(t > max) {
15801                 cell.className = " fc-state-disabled";
15802                 cell.title = cal.maxText;
15803                 return;
15804             }
15805             if(ddays){
15806                 if(ddays.indexOf(d.getDay()) != -1){
15807                     cell.title = ddaysText;
15808                     cell.className = " fc-state-disabled";
15809                 }
15810             }
15811             if(ddMatch && format){
15812                 var fvalue = d.dateFormat(format);
15813                 if(ddMatch.test(fvalue)){
15814                     cell.title = ddText.replace("%0", fvalue);
15815                     cell.className = " fc-state-disabled";
15816                 }
15817             }
15818             
15819             if (!cell.initialClassName) {
15820                 cell.initialClassName = cell.dom.className;
15821             }
15822             
15823             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15824         };
15825
15826         var i = 0;
15827         
15828         for(; i < startingPos; i++) {
15829             textEls[i].innerHTML = (++prevStart);
15830             d.setDate(d.getDate()+1);
15831             
15832             cells[i].className = "fc-past fc-other-month";
15833             setCellClass(this, cells[i]);
15834         }
15835         
15836         var intDay = 0;
15837         
15838         for(; i < days; i++){
15839             intDay = i - startingPos + 1;
15840             textEls[i].innerHTML = (intDay);
15841             d.setDate(d.getDate()+1);
15842             
15843             cells[i].className = ''; // "x-date-active";
15844             setCellClass(this, cells[i]);
15845         }
15846         var extraDays = 0;
15847         
15848         for(; i < 42; i++) {
15849             textEls[i].innerHTML = (++extraDays);
15850             d.setDate(d.getDate()+1);
15851             
15852             cells[i].className = "fc-future fc-other-month";
15853             setCellClass(this, cells[i]);
15854         }
15855         
15856         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15857         
15858         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15859         
15860         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15861         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15862         
15863         if(totalRows != 6){
15864             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15865             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15866         }
15867         
15868         this.fireEvent('monthchange', this, date);
15869         
15870         
15871         /*
15872         if(!this.internalRender){
15873             var main = this.el.dom.firstChild;
15874             var w = main.offsetWidth;
15875             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15876             Roo.fly(main).setWidth(w);
15877             this.internalRender = true;
15878             // opera does not respect the auto grow header center column
15879             // then, after it gets a width opera refuses to recalculate
15880             // without a second pass
15881             if(Roo.isOpera && !this.secondPass){
15882                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15883                 this.secondPass = true;
15884                 this.update.defer(10, this, [date]);
15885             }
15886         }
15887         */
15888         
15889     },
15890     
15891     findCell : function(dt) {
15892         dt = dt.clearTime().getTime();
15893         var ret = false;
15894         this.cells.each(function(c){
15895             //Roo.log("check " +c.dateValue + '?=' + dt);
15896             if(c.dateValue == dt){
15897                 ret = c;
15898                 return false;
15899             }
15900             return true;
15901         });
15902         
15903         return ret;
15904     },
15905     
15906     findCells : function(ev) {
15907         var s = ev.start.clone().clearTime().getTime();
15908        // Roo.log(s);
15909         var e= ev.end.clone().clearTime().getTime();
15910        // Roo.log(e);
15911         var ret = [];
15912         this.cells.each(function(c){
15913              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15914             
15915             if(c.dateValue > e){
15916                 return ;
15917             }
15918             if(c.dateValue < s){
15919                 return ;
15920             }
15921             ret.push(c);
15922         });
15923         
15924         return ret;    
15925     },
15926     
15927 //    findBestRow: function(cells)
15928 //    {
15929 //        var ret = 0;
15930 //        
15931 //        for (var i =0 ; i < cells.length;i++) {
15932 //            ret  = Math.max(cells[i].rows || 0,ret);
15933 //        }
15934 //        return ret;
15935 //        
15936 //    },
15937     
15938     
15939     addItem : function(ev)
15940     {
15941         // look for vertical location slot in
15942         var cells = this.findCells(ev);
15943         
15944 //        ev.row = this.findBestRow(cells);
15945         
15946         // work out the location.
15947         
15948         var crow = false;
15949         var rows = [];
15950         for(var i =0; i < cells.length; i++) {
15951             
15952             cells[i].row = cells[0].row;
15953             
15954             if(i == 0){
15955                 cells[i].row = cells[i].row + 1;
15956             }
15957             
15958             if (!crow) {
15959                 crow = {
15960                     start : cells[i],
15961                     end :  cells[i]
15962                 };
15963                 continue;
15964             }
15965             if (crow.start.getY() == cells[i].getY()) {
15966                 // on same row.
15967                 crow.end = cells[i];
15968                 continue;
15969             }
15970             // different row.
15971             rows.push(crow);
15972             crow = {
15973                 start: cells[i],
15974                 end : cells[i]
15975             };
15976             
15977         }
15978         
15979         rows.push(crow);
15980         ev.els = [];
15981         ev.rows = rows;
15982         ev.cells = cells;
15983         
15984         cells[0].events.push(ev);
15985         
15986         this.calevents.push(ev);
15987     },
15988     
15989     clearEvents: function() {
15990         
15991         if(!this.calevents){
15992             return;
15993         }
15994         
15995         Roo.each(this.cells.elements, function(c){
15996             c.row = 0;
15997             c.events = [];
15998             c.more = [];
15999         });
16000         
16001         Roo.each(this.calevents, function(e) {
16002             Roo.each(e.els, function(el) {
16003                 el.un('mouseenter' ,this.onEventEnter, this);
16004                 el.un('mouseleave' ,this.onEventLeave, this);
16005                 el.remove();
16006             },this);
16007         },this);
16008         
16009         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16010             e.remove();
16011         });
16012         
16013     },
16014     
16015     renderEvents: function()
16016     {   
16017         var _this = this;
16018         
16019         this.cells.each(function(c) {
16020             
16021             if(c.row < 5){
16022                 return;
16023             }
16024             
16025             var ev = c.events;
16026             
16027             var r = 4;
16028             if(c.row != c.events.length){
16029                 r = 4 - (4 - (c.row - c.events.length));
16030             }
16031             
16032             c.events = ev.slice(0, r);
16033             c.more = ev.slice(r);
16034             
16035             if(c.more.length && c.more.length == 1){
16036                 c.events.push(c.more.pop());
16037             }
16038             
16039             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16040             
16041         });
16042             
16043         this.cells.each(function(c) {
16044             
16045             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16046             
16047             
16048             for (var e = 0; e < c.events.length; e++){
16049                 var ev = c.events[e];
16050                 var rows = ev.rows;
16051                 
16052                 for(var i = 0; i < rows.length; i++) {
16053                 
16054                     // how many rows should it span..
16055
16056                     var  cfg = {
16057                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16058                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16059
16060                         unselectable : "on",
16061                         cn : [
16062                             {
16063                                 cls: 'fc-event-inner',
16064                                 cn : [
16065     //                                {
16066     //                                  tag:'span',
16067     //                                  cls: 'fc-event-time',
16068     //                                  html : cells.length > 1 ? '' : ev.time
16069     //                                },
16070                                     {
16071                                       tag:'span',
16072                                       cls: 'fc-event-title',
16073                                       html : String.format('{0}', ev.title)
16074                                     }
16075
16076
16077                                 ]
16078                             },
16079                             {
16080                                 cls: 'ui-resizable-handle ui-resizable-e',
16081                                 html : '&nbsp;&nbsp;&nbsp'
16082                             }
16083
16084                         ]
16085                     };
16086
16087                     if (i == 0) {
16088                         cfg.cls += ' fc-event-start';
16089                     }
16090                     if ((i+1) == rows.length) {
16091                         cfg.cls += ' fc-event-end';
16092                     }
16093
16094                     var ctr = _this.el.select('.fc-event-container',true).first();
16095                     var cg = ctr.createChild(cfg);
16096
16097                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16098                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16099
16100                     var r = (c.more.length) ? 1 : 0;
16101                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16102                     cg.setWidth(ebox.right - sbox.x -2);
16103
16104                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16105                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16106                     cg.on('click', _this.onEventClick, _this, ev);
16107
16108                     ev.els.push(cg);
16109                     
16110                 }
16111                 
16112             }
16113             
16114             
16115             if(c.more.length){
16116                 var  cfg = {
16117                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16118                     style : 'position: absolute',
16119                     unselectable : "on",
16120                     cn : [
16121                         {
16122                             cls: 'fc-event-inner',
16123                             cn : [
16124                                 {
16125                                   tag:'span',
16126                                   cls: 'fc-event-title',
16127                                   html : 'More'
16128                                 }
16129
16130
16131                             ]
16132                         },
16133                         {
16134                             cls: 'ui-resizable-handle ui-resizable-e',
16135                             html : '&nbsp;&nbsp;&nbsp'
16136                         }
16137
16138                     ]
16139                 };
16140
16141                 var ctr = _this.el.select('.fc-event-container',true).first();
16142                 var cg = ctr.createChild(cfg);
16143
16144                 var sbox = c.select('.fc-day-content',true).first().getBox();
16145                 var ebox = c.select('.fc-day-content',true).first().getBox();
16146                 //Roo.log(cg);
16147                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16148                 cg.setWidth(ebox.right - sbox.x -2);
16149
16150                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16151                 
16152             }
16153             
16154         });
16155         
16156         
16157         
16158     },
16159     
16160     onEventEnter: function (e, el,event,d) {
16161         this.fireEvent('evententer', this, el, event);
16162     },
16163     
16164     onEventLeave: function (e, el,event,d) {
16165         this.fireEvent('eventleave', this, el, event);
16166     },
16167     
16168     onEventClick: function (e, el,event,d) {
16169         this.fireEvent('eventclick', this, el, event);
16170     },
16171     
16172     onMonthChange: function () {
16173         this.store.load();
16174     },
16175     
16176     onMoreEventClick: function(e, el, more)
16177     {
16178         var _this = this;
16179         
16180         this.calpopover.placement = 'right';
16181         this.calpopover.setTitle('More');
16182         
16183         this.calpopover.setContent('');
16184         
16185         var ctr = this.calpopover.el.select('.popover-content', true).first();
16186         
16187         Roo.each(more, function(m){
16188             var cfg = {
16189                 cls : 'fc-event-hori fc-event-draggable',
16190                 html : m.title
16191             };
16192             var cg = ctr.createChild(cfg);
16193             
16194             cg.on('click', _this.onEventClick, _this, m);
16195         });
16196         
16197         this.calpopover.show(el);
16198         
16199         
16200     },
16201     
16202     onLoad: function () 
16203     {   
16204         this.calevents = [];
16205         var cal = this;
16206         
16207         if(this.store.getCount() > 0){
16208             this.store.data.each(function(d){
16209                cal.addItem({
16210                     id : d.data.id,
16211                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16212                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16213                     time : d.data.start_time,
16214                     title : d.data.title,
16215                     description : d.data.description,
16216                     venue : d.data.venue
16217                 });
16218             });
16219         }
16220         
16221         this.renderEvents();
16222         
16223         if(this.calevents.length && this.loadMask){
16224             this.maskEl.hide();
16225         }
16226     },
16227     
16228     onBeforeLoad: function()
16229     {
16230         this.clearEvents();
16231         if(this.loadMask){
16232             this.maskEl.show();
16233         }
16234     }
16235 });
16236
16237  
16238  /*
16239  * - LGPL
16240  *
16241  * element
16242  * 
16243  */
16244
16245 /**
16246  * @class Roo.bootstrap.Popover
16247  * @extends Roo.bootstrap.Component
16248  * Bootstrap Popover class
16249  * @cfg {String} html contents of the popover   (or false to use children..)
16250  * @cfg {String} title of popover (or false to hide)
16251  * @cfg {String} placement how it is placed
16252  * @cfg {String} trigger click || hover (or false to trigger manually)
16253  * @cfg {String} over what (parent or false to trigger manually.)
16254  * @cfg {Number} delay - delay before showing
16255  
16256  * @constructor
16257  * Create a new Popover
16258  * @param {Object} config The config object
16259  */
16260
16261 Roo.bootstrap.Popover = function(config){
16262     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16263     
16264     this.addEvents({
16265         // raw events
16266          /**
16267          * @event show
16268          * After the popover show
16269          * 
16270          * @param {Roo.bootstrap.Popover} this
16271          */
16272         "show" : true,
16273         /**
16274          * @event hide
16275          * After the popover hide
16276          * 
16277          * @param {Roo.bootstrap.Popover} this
16278          */
16279         "hide" : true
16280     });
16281 };
16282
16283 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16284     
16285     title: 'Fill in a title',
16286     html: false,
16287     
16288     placement : 'right',
16289     trigger : 'hover', // hover
16290     
16291     delay : 0,
16292     
16293     over: 'parent',
16294     
16295     can_build_overlaid : false,
16296     
16297     getChildContainer : function()
16298     {
16299         return this.el.select('.popover-content',true).first();
16300     },
16301     
16302     getAutoCreate : function(){
16303          
16304         var cfg = {
16305            cls : 'popover roo-dynamic',
16306            style: 'display:block',
16307            cn : [
16308                 {
16309                     cls : 'arrow'
16310                 },
16311                 {
16312                     cls : 'popover-inner',
16313                     cn : [
16314                         {
16315                             tag: 'h3',
16316                             cls: 'popover-title',
16317                             html : this.title
16318                         },
16319                         {
16320                             cls : 'popover-content',
16321                             html : this.html
16322                         }
16323                     ]
16324                     
16325                 }
16326            ]
16327         };
16328         
16329         return cfg;
16330     },
16331     setTitle: function(str)
16332     {
16333         this.title = str;
16334         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16335     },
16336     setContent: function(str)
16337     {
16338         this.html = str;
16339         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16340     },
16341     // as it get's added to the bottom of the page.
16342     onRender : function(ct, position)
16343     {
16344         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16345         if(!this.el){
16346             var cfg = Roo.apply({},  this.getAutoCreate());
16347             cfg.id = Roo.id();
16348             
16349             if (this.cls) {
16350                 cfg.cls += ' ' + this.cls;
16351             }
16352             if (this.style) {
16353                 cfg.style = this.style;
16354             }
16355             //Roo.log("adding to ");
16356             this.el = Roo.get(document.body).createChild(cfg, position);
16357 //            Roo.log(this.el);
16358         }
16359         this.initEvents();
16360     },
16361     
16362     initEvents : function()
16363     {
16364         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16365         this.el.enableDisplayMode('block');
16366         this.el.hide();
16367         if (this.over === false) {
16368             return; 
16369         }
16370         if (this.triggers === false) {
16371             return;
16372         }
16373         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16374         var triggers = this.trigger ? this.trigger.split(' ') : [];
16375         Roo.each(triggers, function(trigger) {
16376         
16377             if (trigger == 'click') {
16378                 on_el.on('click', this.toggle, this);
16379             } else if (trigger != 'manual') {
16380                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16381                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16382       
16383                 on_el.on(eventIn  ,this.enter, this);
16384                 on_el.on(eventOut, this.leave, this);
16385             }
16386         }, this);
16387         
16388     },
16389     
16390     
16391     // private
16392     timeout : null,
16393     hoverState : null,
16394     
16395     toggle : function () {
16396         this.hoverState == 'in' ? this.leave() : this.enter();
16397     },
16398     
16399     enter : function () {
16400         
16401         clearTimeout(this.timeout);
16402     
16403         this.hoverState = 'in';
16404     
16405         if (!this.delay || !this.delay.show) {
16406             this.show();
16407             return;
16408         }
16409         var _t = this;
16410         this.timeout = setTimeout(function () {
16411             if (_t.hoverState == 'in') {
16412                 _t.show();
16413             }
16414         }, this.delay.show)
16415     },
16416     
16417     leave : function() {
16418         clearTimeout(this.timeout);
16419     
16420         this.hoverState = 'out';
16421     
16422         if (!this.delay || !this.delay.hide) {
16423             this.hide();
16424             return;
16425         }
16426         var _t = this;
16427         this.timeout = setTimeout(function () {
16428             if (_t.hoverState == 'out') {
16429                 _t.hide();
16430             }
16431         }, this.delay.hide)
16432     },
16433     
16434     show : function (on_el)
16435     {
16436         if (!on_el) {
16437             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16438         }
16439         
16440         // set content.
16441         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16442         if (this.html !== false) {
16443             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16444         }
16445         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16446         if (!this.title.length) {
16447             this.el.select('.popover-title',true).hide();
16448         }
16449         
16450         var placement = typeof this.placement == 'function' ?
16451             this.placement.call(this, this.el, on_el) :
16452             this.placement;
16453             
16454         var autoToken = /\s?auto?\s?/i;
16455         var autoPlace = autoToken.test(placement);
16456         if (autoPlace) {
16457             placement = placement.replace(autoToken, '') || 'top';
16458         }
16459         
16460         //this.el.detach()
16461         //this.el.setXY([0,0]);
16462         this.el.show();
16463         this.el.dom.style.display='block';
16464         this.el.addClass(placement);
16465         
16466         //this.el.appendTo(on_el);
16467         
16468         var p = this.getPosition();
16469         var box = this.el.getBox();
16470         
16471         if (autoPlace) {
16472             // fixme..
16473         }
16474         var align = Roo.bootstrap.Popover.alignment[placement];
16475         this.el.alignTo(on_el, align[0],align[1]);
16476         //var arrow = this.el.select('.arrow',true).first();
16477         //arrow.set(align[2], 
16478         
16479         this.el.addClass('in');
16480         
16481         
16482         if (this.el.hasClass('fade')) {
16483             // fade it?
16484         }
16485         
16486         this.hoverState = 'in';
16487         
16488         this.fireEvent('show', this);
16489         
16490     },
16491     hide : function()
16492     {
16493         this.el.setXY([0,0]);
16494         this.el.removeClass('in');
16495         this.el.hide();
16496         this.hoverState = null;
16497         
16498         this.fireEvent('hide', this);
16499     }
16500     
16501 });
16502
16503 Roo.bootstrap.Popover.alignment = {
16504     'left' : ['r-l', [-10,0], 'right'],
16505     'right' : ['l-r', [10,0], 'left'],
16506     'bottom' : ['t-b', [0,10], 'top'],
16507     'top' : [ 'b-t', [0,-10], 'bottom']
16508 };
16509
16510  /*
16511  * - LGPL
16512  *
16513  * Progress
16514  * 
16515  */
16516
16517 /**
16518  * @class Roo.bootstrap.Progress
16519  * @extends Roo.bootstrap.Component
16520  * Bootstrap Progress class
16521  * @cfg {Boolean} striped striped of the progress bar
16522  * @cfg {Boolean} active animated of the progress bar
16523  * 
16524  * 
16525  * @constructor
16526  * Create a new Progress
16527  * @param {Object} config The config object
16528  */
16529
16530 Roo.bootstrap.Progress = function(config){
16531     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16532 };
16533
16534 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16535     
16536     striped : false,
16537     active: false,
16538     
16539     getAutoCreate : function(){
16540         var cfg = {
16541             tag: 'div',
16542             cls: 'progress'
16543         };
16544         
16545         
16546         if(this.striped){
16547             cfg.cls += ' progress-striped';
16548         }
16549       
16550         if(this.active){
16551             cfg.cls += ' active';
16552         }
16553         
16554         
16555         return cfg;
16556     }
16557    
16558 });
16559
16560  
16561
16562  /*
16563  * - LGPL
16564  *
16565  * ProgressBar
16566  * 
16567  */
16568
16569 /**
16570  * @class Roo.bootstrap.ProgressBar
16571  * @extends Roo.bootstrap.Component
16572  * Bootstrap ProgressBar class
16573  * @cfg {Number} aria_valuenow aria-value now
16574  * @cfg {Number} aria_valuemin aria-value min
16575  * @cfg {Number} aria_valuemax aria-value max
16576  * @cfg {String} label label for the progress bar
16577  * @cfg {String} panel (success | info | warning | danger )
16578  * @cfg {String} role role of the progress bar
16579  * @cfg {String} sr_only text
16580  * 
16581  * 
16582  * @constructor
16583  * Create a new ProgressBar
16584  * @param {Object} config The config object
16585  */
16586
16587 Roo.bootstrap.ProgressBar = function(config){
16588     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16589 };
16590
16591 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16592     
16593     aria_valuenow : 0,
16594     aria_valuemin : 0,
16595     aria_valuemax : 100,
16596     label : false,
16597     panel : false,
16598     role : false,
16599     sr_only: false,
16600     
16601     getAutoCreate : function()
16602     {
16603         
16604         var cfg = {
16605             tag: 'div',
16606             cls: 'progress-bar',
16607             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16608         };
16609         
16610         if(this.sr_only){
16611             cfg.cn = {
16612                 tag: 'span',
16613                 cls: 'sr-only',
16614                 html: this.sr_only
16615             }
16616         }
16617         
16618         if(this.role){
16619             cfg.role = this.role;
16620         }
16621         
16622         if(this.aria_valuenow){
16623             cfg['aria-valuenow'] = this.aria_valuenow;
16624         }
16625         
16626         if(this.aria_valuemin){
16627             cfg['aria-valuemin'] = this.aria_valuemin;
16628         }
16629         
16630         if(this.aria_valuemax){
16631             cfg['aria-valuemax'] = this.aria_valuemax;
16632         }
16633         
16634         if(this.label && !this.sr_only){
16635             cfg.html = this.label;
16636         }
16637         
16638         if(this.panel){
16639             cfg.cls += ' progress-bar-' + this.panel;
16640         }
16641         
16642         return cfg;
16643     },
16644     
16645     update : function(aria_valuenow)
16646     {
16647         this.aria_valuenow = aria_valuenow;
16648         
16649         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16650     }
16651    
16652 });
16653
16654  
16655
16656  /*
16657  * - LGPL
16658  *
16659  * column
16660  * 
16661  */
16662
16663 /**
16664  * @class Roo.bootstrap.TabGroup
16665  * @extends Roo.bootstrap.Column
16666  * Bootstrap Column class
16667  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16668  * @cfg {Boolean} carousel true to make the group behave like a carousel
16669  * @cfg {Boolean} bullets show bullets for the panels
16670  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16671  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16672  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16673  * @cfg {Boolean} showarrow (true|false) show arrow default true
16674  * 
16675  * @constructor
16676  * Create a new TabGroup
16677  * @param {Object} config The config object
16678  */
16679
16680 Roo.bootstrap.TabGroup = function(config){
16681     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16682     if (!this.navId) {
16683         this.navId = Roo.id();
16684     }
16685     this.tabs = [];
16686     Roo.bootstrap.TabGroup.register(this);
16687     
16688 };
16689
16690 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16691     
16692     carousel : false,
16693     transition : false,
16694     bullets : 0,
16695     timer : 0,
16696     autoslide : false,
16697     slideFn : false,
16698     slideOnTouch : false,
16699     showarrow : true,
16700     
16701     getAutoCreate : function()
16702     {
16703         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16704         
16705         cfg.cls += ' tab-content';
16706         
16707         if (this.carousel) {
16708             cfg.cls += ' carousel slide';
16709             
16710             cfg.cn = [{
16711                cls : 'carousel-inner',
16712                cn : []
16713             }];
16714         
16715             if(this.bullets  && !Roo.isTouch){
16716                 
16717                 var bullets = {
16718                     cls : 'carousel-bullets',
16719                     cn : []
16720                 };
16721                
16722                 if(this.bullets_cls){
16723                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16724                 }
16725                 
16726                 bullets.cn.push({
16727                     cls : 'clear'
16728                 });
16729                 
16730                 cfg.cn[0].cn.push(bullets);
16731             }
16732             
16733             if(this.showarrow){
16734                 cfg.cn[0].cn.push({
16735                     tag : 'div',
16736                     class : 'carousel-arrow',
16737                     cn : [
16738                         {
16739                             tag : 'div',
16740                             class : 'carousel-prev',
16741                             cn : [
16742                                 {
16743                                     tag : 'i',
16744                                     class : 'fa fa-chevron-left'
16745                                 }
16746                             ]
16747                         },
16748                         {
16749                             tag : 'div',
16750                             class : 'carousel-next',
16751                             cn : [
16752                                 {
16753                                     tag : 'i',
16754                                     class : 'fa fa-chevron-right'
16755                                 }
16756                             ]
16757                         }
16758                     ]
16759                 });
16760             }
16761             
16762         }
16763         
16764         return cfg;
16765     },
16766     
16767     initEvents:  function()
16768     {
16769         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16770             this.el.on("touchstart", this.onTouchStart, this);
16771         }
16772         
16773         if(this.autoslide){
16774             var _this = this;
16775             
16776             this.slideFn = window.setInterval(function() {
16777                 _this.showPanelNext();
16778             }, this.timer);
16779         }
16780         
16781         if(this.showarrow){
16782             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16783             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16784         }
16785         
16786         
16787     },
16788     
16789     onTouchStart : function(e, el, o)
16790     {
16791         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16792             return;
16793         }
16794         
16795         this.showPanelNext();
16796     },
16797     
16798     getChildContainer : function()
16799     {
16800         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16801     },
16802     
16803     /**
16804     * register a Navigation item
16805     * @param {Roo.bootstrap.NavItem} the navitem to add
16806     */
16807     register : function(item)
16808     {
16809         this.tabs.push( item);
16810         item.navId = this.navId; // not really needed..
16811         this.addBullet();
16812     
16813     },
16814     
16815     getActivePanel : function()
16816     {
16817         var r = false;
16818         Roo.each(this.tabs, function(t) {
16819             if (t.active) {
16820                 r = t;
16821                 return false;
16822             }
16823             return null;
16824         });
16825         return r;
16826         
16827     },
16828     getPanelByName : function(n)
16829     {
16830         var r = false;
16831         Roo.each(this.tabs, function(t) {
16832             if (t.tabId == n) {
16833                 r = t;
16834                 return false;
16835             }
16836             return null;
16837         });
16838         return r;
16839     },
16840     indexOfPanel : function(p)
16841     {
16842         var r = false;
16843         Roo.each(this.tabs, function(t,i) {
16844             if (t.tabId == p.tabId) {
16845                 r = i;
16846                 return false;
16847             }
16848             return null;
16849         });
16850         return r;
16851     },
16852     /**
16853      * show a specific panel
16854      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16855      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16856      */
16857     showPanel : function (pan)
16858     {
16859         if(this.transition || typeof(pan) == 'undefined'){
16860             Roo.log("waiting for the transitionend");
16861             return;
16862         }
16863         
16864         if (typeof(pan) == 'number') {
16865             pan = this.tabs[pan];
16866         }
16867         
16868         if (typeof(pan) == 'string') {
16869             pan = this.getPanelByName(pan);
16870         }
16871         
16872         var cur = this.getActivePanel();
16873         
16874         if(!pan || !cur){
16875             Roo.log('pan or acitve pan is undefined');
16876             return false;
16877         }
16878         
16879         if (pan.tabId == this.getActivePanel().tabId) {
16880             return true;
16881         }
16882         
16883         if (false === cur.fireEvent('beforedeactivate')) {
16884             return false;
16885         }
16886         
16887         if(this.bullets > 0 && !Roo.isTouch){
16888             this.setActiveBullet(this.indexOfPanel(pan));
16889         }
16890         
16891         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16892             
16893             this.transition = true;
16894             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16895             var lr = dir == 'next' ? 'left' : 'right';
16896             pan.el.addClass(dir); // or prev
16897             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16898             cur.el.addClass(lr); // or right
16899             pan.el.addClass(lr);
16900             
16901             var _this = this;
16902             cur.el.on('transitionend', function() {
16903                 Roo.log("trans end?");
16904                 
16905                 pan.el.removeClass([lr,dir]);
16906                 pan.setActive(true);
16907                 
16908                 cur.el.removeClass([lr]);
16909                 cur.setActive(false);
16910                 
16911                 _this.transition = false;
16912                 
16913             }, this, { single:  true } );
16914             
16915             return true;
16916         }
16917         
16918         cur.setActive(false);
16919         pan.setActive(true);
16920         
16921         return true;
16922         
16923     },
16924     showPanelNext : function()
16925     {
16926         var i = this.indexOfPanel(this.getActivePanel());
16927         
16928         if (i >= this.tabs.length - 1 && !this.autoslide) {
16929             return;
16930         }
16931         
16932         if (i >= this.tabs.length - 1 && this.autoslide) {
16933             i = -1;
16934         }
16935         
16936         this.showPanel(this.tabs[i+1]);
16937     },
16938     
16939     showPanelPrev : function()
16940     {
16941         var i = this.indexOfPanel(this.getActivePanel());
16942         
16943         if (i  < 1 && !this.autoslide) {
16944             return;
16945         }
16946         
16947         if (i < 1 && this.autoslide) {
16948             i = this.tabs.length;
16949         }
16950         
16951         this.showPanel(this.tabs[i-1]);
16952     },
16953     
16954     
16955     addBullet: function()
16956     {
16957         if(!this.bullets || Roo.isTouch){
16958             return;
16959         }
16960         var ctr = this.el.select('.carousel-bullets',true).first();
16961         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16962         var bullet = ctr.createChild({
16963             cls : 'bullet bullet-' + i
16964         },ctr.dom.lastChild);
16965         
16966         
16967         var _this = this;
16968         
16969         bullet.on('click', (function(e, el, o, ii, t){
16970
16971             e.preventDefault();
16972
16973             this.showPanel(ii);
16974
16975             if(this.autoslide && this.slideFn){
16976                 clearInterval(this.slideFn);
16977                 this.slideFn = window.setInterval(function() {
16978                     _this.showPanelNext();
16979                 }, this.timer);
16980             }
16981
16982         }).createDelegate(this, [i, bullet], true));
16983                 
16984         
16985     },
16986      
16987     setActiveBullet : function(i)
16988     {
16989         if(Roo.isTouch){
16990             return;
16991         }
16992         
16993         Roo.each(this.el.select('.bullet', true).elements, function(el){
16994             el.removeClass('selected');
16995         });
16996
16997         var bullet = this.el.select('.bullet-' + i, true).first();
16998         
16999         if(!bullet){
17000             return;
17001         }
17002         
17003         bullet.addClass('selected');
17004     }
17005     
17006     
17007   
17008 });
17009
17010  
17011
17012  
17013  
17014 Roo.apply(Roo.bootstrap.TabGroup, {
17015     
17016     groups: {},
17017      /**
17018     * register a Navigation Group
17019     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17020     */
17021     register : function(navgrp)
17022     {
17023         this.groups[navgrp.navId] = navgrp;
17024         
17025     },
17026     /**
17027     * fetch a Navigation Group based on the navigation ID
17028     * if one does not exist , it will get created.
17029     * @param {string} the navgroup to add
17030     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17031     */
17032     get: function(navId) {
17033         if (typeof(this.groups[navId]) == 'undefined') {
17034             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17035         }
17036         return this.groups[navId] ;
17037     }
17038     
17039     
17040     
17041 });
17042
17043  /*
17044  * - LGPL
17045  *
17046  * TabPanel
17047  * 
17048  */
17049
17050 /**
17051  * @class Roo.bootstrap.TabPanel
17052  * @extends Roo.bootstrap.Component
17053  * Bootstrap TabPanel class
17054  * @cfg {Boolean} active panel active
17055  * @cfg {String} html panel content
17056  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17057  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17058  * @cfg {String} href click to link..
17059  * 
17060  * 
17061  * @constructor
17062  * Create a new TabPanel
17063  * @param {Object} config The config object
17064  */
17065
17066 Roo.bootstrap.TabPanel = function(config){
17067     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17068     this.addEvents({
17069         /**
17070              * @event changed
17071              * Fires when the active status changes
17072              * @param {Roo.bootstrap.TabPanel} this
17073              * @param {Boolean} state the new state
17074             
17075          */
17076         'changed': true,
17077         /**
17078              * @event beforedeactivate
17079              * Fires before a tab is de-activated - can be used to do validation on a form.
17080              * @param {Roo.bootstrap.TabPanel} this
17081              * @return {Boolean} false if there is an error
17082             
17083          */
17084         'beforedeactivate': true
17085      });
17086     
17087     this.tabId = this.tabId || Roo.id();
17088   
17089 };
17090
17091 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17092     
17093     active: false,
17094     html: false,
17095     tabId: false,
17096     navId : false,
17097     href : '',
17098     
17099     getAutoCreate : function(){
17100         var cfg = {
17101             tag: 'div',
17102             // item is needed for carousel - not sure if it has any effect otherwise
17103             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17104             html: this.html || ''
17105         };
17106         
17107         if(this.active){
17108             cfg.cls += ' active';
17109         }
17110         
17111         if(this.tabId){
17112             cfg.tabId = this.tabId;
17113         }
17114         
17115         
17116         return cfg;
17117     },
17118     
17119     initEvents:  function()
17120     {
17121         var p = this.parent();
17122         this.navId = this.navId || p.navId;
17123         
17124         if (typeof(this.navId) != 'undefined') {
17125             // not really needed.. but just in case.. parent should be a NavGroup.
17126             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17127             
17128             tg.register(this);
17129             
17130             var i = tg.tabs.length - 1;
17131             
17132             if(this.active && tg.bullets > 0 && i < tg.bullets){
17133                 tg.setActiveBullet(i);
17134             }
17135         }
17136         
17137         if(this.href.length){
17138             this.el.on('click', this.onClick, this);
17139         }
17140         
17141     },
17142     
17143     onRender : function(ct, position)
17144     {
17145        // Roo.log("Call onRender: " + this.xtype);
17146         
17147         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17148         
17149         
17150         
17151         
17152         
17153     },
17154     
17155     setActive: function(state)
17156     {
17157         Roo.log("panel - set active " + this.tabId + "=" + state);
17158         
17159         this.active = state;
17160         if (!state) {
17161             this.el.removeClass('active');
17162             
17163         } else  if (!this.el.hasClass('active')) {
17164             this.el.addClass('active');
17165         }
17166         
17167         this.fireEvent('changed', this, state);
17168     },
17169     
17170     onClick: function(e)
17171     {
17172         e.preventDefault();
17173         
17174         window.location.href = this.href;
17175     }
17176     
17177     
17178 });
17179  
17180
17181  
17182
17183  /*
17184  * - LGPL
17185  *
17186  * DateField
17187  * 
17188  */
17189
17190 /**
17191  * @class Roo.bootstrap.DateField
17192  * @extends Roo.bootstrap.Input
17193  * Bootstrap DateField class
17194  * @cfg {Number} weekStart default 0
17195  * @cfg {String} viewMode default empty, (months|years)
17196  * @cfg {String} minViewMode default empty, (months|years)
17197  * @cfg {Number} startDate default -Infinity
17198  * @cfg {Number} endDate default Infinity
17199  * @cfg {Boolean} todayHighlight default false
17200  * @cfg {Boolean} todayBtn default false
17201  * @cfg {Boolean} calendarWeeks default false
17202  * @cfg {Object} daysOfWeekDisabled default empty
17203  * @cfg {Boolean} singleMode default false (true | false)
17204  * 
17205  * @cfg {Boolean} keyboardNavigation default true
17206  * @cfg {String} language default en
17207  * 
17208  * @constructor
17209  * Create a new DateField
17210  * @param {Object} config The config object
17211  */
17212
17213 Roo.bootstrap.DateField = function(config){
17214     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17215      this.addEvents({
17216             /**
17217              * @event show
17218              * Fires when this field show.
17219              * @param {Roo.bootstrap.DateField} this
17220              * @param {Mixed} date The date value
17221              */
17222             show : true,
17223             /**
17224              * @event show
17225              * Fires when this field hide.
17226              * @param {Roo.bootstrap.DateField} this
17227              * @param {Mixed} date The date value
17228              */
17229             hide : true,
17230             /**
17231              * @event select
17232              * Fires when select a date.
17233              * @param {Roo.bootstrap.DateField} this
17234              * @param {Mixed} date The date value
17235              */
17236             select : true,
17237             /**
17238              * @event beforeselect
17239              * Fires when before select a date.
17240              * @param {Roo.bootstrap.DateField} this
17241              * @param {Mixed} date The date value
17242              */
17243             beforeselect : true
17244         });
17245 };
17246
17247 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17248     
17249     /**
17250      * @cfg {String} format
17251      * The default date format string which can be overriden for localization support.  The format must be
17252      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17253      */
17254     format : "m/d/y",
17255     /**
17256      * @cfg {String} altFormats
17257      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17258      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17259      */
17260     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17261     
17262     weekStart : 0,
17263     
17264     viewMode : '',
17265     
17266     minViewMode : '',
17267     
17268     todayHighlight : false,
17269     
17270     todayBtn: false,
17271     
17272     language: 'en',
17273     
17274     keyboardNavigation: true,
17275     
17276     calendarWeeks: false,
17277     
17278     startDate: -Infinity,
17279     
17280     endDate: Infinity,
17281     
17282     daysOfWeekDisabled: [],
17283     
17284     _events: [],
17285     
17286     singleMode : false,
17287     
17288     UTCDate: function()
17289     {
17290         return new Date(Date.UTC.apply(Date, arguments));
17291     },
17292     
17293     UTCToday: function()
17294     {
17295         var today = new Date();
17296         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17297     },
17298     
17299     getDate: function() {
17300             var d = this.getUTCDate();
17301             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17302     },
17303     
17304     getUTCDate: function() {
17305             return this.date;
17306     },
17307     
17308     setDate: function(d) {
17309             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17310     },
17311     
17312     setUTCDate: function(d) {
17313             this.date = d;
17314             this.setValue(this.formatDate(this.date));
17315     },
17316         
17317     onRender: function(ct, position)
17318     {
17319         
17320         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17321         
17322         this.language = this.language || 'en';
17323         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17324         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17325         
17326         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17327         this.format = this.format || 'm/d/y';
17328         this.isInline = false;
17329         this.isInput = true;
17330         this.component = this.el.select('.add-on', true).first() || false;
17331         this.component = (this.component && this.component.length === 0) ? false : this.component;
17332         this.hasInput = this.component && this.inputEL().length;
17333         
17334         if (typeof(this.minViewMode === 'string')) {
17335             switch (this.minViewMode) {
17336                 case 'months':
17337                     this.minViewMode = 1;
17338                     break;
17339                 case 'years':
17340                     this.minViewMode = 2;
17341                     break;
17342                 default:
17343                     this.minViewMode = 0;
17344                     break;
17345             }
17346         }
17347         
17348         if (typeof(this.viewMode === 'string')) {
17349             switch (this.viewMode) {
17350                 case 'months':
17351                     this.viewMode = 1;
17352                     break;
17353                 case 'years':
17354                     this.viewMode = 2;
17355                     break;
17356                 default:
17357                     this.viewMode = 0;
17358                     break;
17359             }
17360         }
17361                 
17362         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17363         
17364 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17365         
17366         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17367         
17368         this.picker().on('mousedown', this.onMousedown, this);
17369         this.picker().on('click', this.onClick, this);
17370         
17371         this.picker().addClass('datepicker-dropdown');
17372         
17373         this.startViewMode = this.viewMode;
17374         
17375         if(this.singleMode){
17376             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17377                 v.setVisibilityMode(Roo.Element.DISPLAY);
17378                 v.hide();
17379             });
17380             
17381             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17382                 v.setStyle('width', '189px');
17383             });
17384         }
17385         
17386         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17387             if(!this.calendarWeeks){
17388                 v.remove();
17389                 return;
17390             }
17391             
17392             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17393             v.attr('colspan', function(i, val){
17394                 return parseInt(val) + 1;
17395             });
17396         });
17397                         
17398         
17399         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17400         
17401         this.setStartDate(this.startDate);
17402         this.setEndDate(this.endDate);
17403         
17404         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17405         
17406         this.fillDow();
17407         this.fillMonths();
17408         this.update();
17409         this.showMode();
17410         
17411         if(this.isInline) {
17412             this.show();
17413         }
17414     },
17415     
17416     picker : function()
17417     {
17418         return this.pickerEl;
17419 //        return this.el.select('.datepicker', true).first();
17420     },
17421     
17422     fillDow: function()
17423     {
17424         var dowCnt = this.weekStart;
17425         
17426         var dow = {
17427             tag: 'tr',
17428             cn: [
17429                 
17430             ]
17431         };
17432         
17433         if(this.calendarWeeks){
17434             dow.cn.push({
17435                 tag: 'th',
17436                 cls: 'cw',
17437                 html: '&nbsp;'
17438             })
17439         }
17440         
17441         while (dowCnt < this.weekStart + 7) {
17442             dow.cn.push({
17443                 tag: 'th',
17444                 cls: 'dow',
17445                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17446             });
17447         }
17448         
17449         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17450     },
17451     
17452     fillMonths: function()
17453     {    
17454         var i = 0;
17455         var months = this.picker().select('>.datepicker-months td', true).first();
17456         
17457         months.dom.innerHTML = '';
17458         
17459         while (i < 12) {
17460             var month = {
17461                 tag: 'span',
17462                 cls: 'month',
17463                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17464             };
17465             
17466             months.createChild(month);
17467         }
17468         
17469     },
17470     
17471     update: function()
17472     {
17473         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;
17474         
17475         if (this.date < this.startDate) {
17476             this.viewDate = new Date(this.startDate);
17477         } else if (this.date > this.endDate) {
17478             this.viewDate = new Date(this.endDate);
17479         } else {
17480             this.viewDate = new Date(this.date);
17481         }
17482         
17483         this.fill();
17484     },
17485     
17486     fill: function() 
17487     {
17488         var d = new Date(this.viewDate),
17489                 year = d.getUTCFullYear(),
17490                 month = d.getUTCMonth(),
17491                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17492                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17493                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17494                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17495                 currentDate = this.date && this.date.valueOf(),
17496                 today = this.UTCToday();
17497         
17498         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17499         
17500 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17501         
17502 //        this.picker.select('>tfoot th.today').
17503 //                                              .text(dates[this.language].today)
17504 //                                              .toggle(this.todayBtn !== false);
17505     
17506         this.updateNavArrows();
17507         this.fillMonths();
17508                                                 
17509         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17510         
17511         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17512          
17513         prevMonth.setUTCDate(day);
17514         
17515         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17516         
17517         var nextMonth = new Date(prevMonth);
17518         
17519         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17520         
17521         nextMonth = nextMonth.valueOf();
17522         
17523         var fillMonths = false;
17524         
17525         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17526         
17527         while(prevMonth.valueOf() < nextMonth) {
17528             var clsName = '';
17529             
17530             if (prevMonth.getUTCDay() === this.weekStart) {
17531                 if(fillMonths){
17532                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17533                 }
17534                     
17535                 fillMonths = {
17536                     tag: 'tr',
17537                     cn: []
17538                 };
17539                 
17540                 if(this.calendarWeeks){
17541                     // ISO 8601: First week contains first thursday.
17542                     // ISO also states week starts on Monday, but we can be more abstract here.
17543                     var
17544                     // Start of current week: based on weekstart/current date
17545                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17546                     // Thursday of this week
17547                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17548                     // First Thursday of year, year from thursday
17549                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17550                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17551                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17552                     
17553                     fillMonths.cn.push({
17554                         tag: 'td',
17555                         cls: 'cw',
17556                         html: calWeek
17557                     });
17558                 }
17559             }
17560             
17561             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17562                 clsName += ' old';
17563             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17564                 clsName += ' new';
17565             }
17566             if (this.todayHighlight &&
17567                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17568                 prevMonth.getUTCMonth() == today.getMonth() &&
17569                 prevMonth.getUTCDate() == today.getDate()) {
17570                 clsName += ' today';
17571             }
17572             
17573             if (currentDate && prevMonth.valueOf() === currentDate) {
17574                 clsName += ' active';
17575             }
17576             
17577             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17578                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17579                     clsName += ' disabled';
17580             }
17581             
17582             fillMonths.cn.push({
17583                 tag: 'td',
17584                 cls: 'day ' + clsName,
17585                 html: prevMonth.getDate()
17586             });
17587             
17588             prevMonth.setDate(prevMonth.getDate()+1);
17589         }
17590           
17591         var currentYear = this.date && this.date.getUTCFullYear();
17592         var currentMonth = this.date && this.date.getUTCMonth();
17593         
17594         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17595         
17596         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17597             v.removeClass('active');
17598             
17599             if(currentYear === year && k === currentMonth){
17600                 v.addClass('active');
17601             }
17602             
17603             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17604                 v.addClass('disabled');
17605             }
17606             
17607         });
17608         
17609         
17610         year = parseInt(year/10, 10) * 10;
17611         
17612         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17613         
17614         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17615         
17616         year -= 1;
17617         for (var i = -1; i < 11; i++) {
17618             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17619                 tag: 'span',
17620                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17621                 html: year
17622             });
17623             
17624             year += 1;
17625         }
17626     },
17627     
17628     showMode: function(dir) 
17629     {
17630         if (dir) {
17631             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17632         }
17633         
17634         Roo.each(this.picker().select('>div',true).elements, function(v){
17635             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17636             v.hide();
17637         });
17638         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17639     },
17640     
17641     place: function()
17642     {
17643         if(this.isInline) {
17644             return;
17645         }
17646         
17647         this.picker().removeClass(['bottom', 'top']);
17648         
17649         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17650             /*
17651              * place to the top of element!
17652              *
17653              */
17654             
17655             this.picker().addClass('top');
17656             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17657             
17658             return;
17659         }
17660         
17661         this.picker().addClass('bottom');
17662         
17663         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17664     },
17665     
17666     parseDate : function(value)
17667     {
17668         if(!value || value instanceof Date){
17669             return value;
17670         }
17671         var v = Date.parseDate(value, this.format);
17672         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17673             v = Date.parseDate(value, 'Y-m-d');
17674         }
17675         if(!v && this.altFormats){
17676             if(!this.altFormatsArray){
17677                 this.altFormatsArray = this.altFormats.split("|");
17678             }
17679             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17680                 v = Date.parseDate(value, this.altFormatsArray[i]);
17681             }
17682         }
17683         return v;
17684     },
17685     
17686     formatDate : function(date, fmt)
17687     {   
17688         return (!date || !(date instanceof Date)) ?
17689         date : date.dateFormat(fmt || this.format);
17690     },
17691     
17692     onFocus : function()
17693     {
17694         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17695         this.show();
17696     },
17697     
17698     onBlur : function()
17699     {
17700         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17701         
17702         var d = this.inputEl().getValue();
17703         
17704         this.setValue(d);
17705                 
17706         this.hide();
17707     },
17708     
17709     show : function()
17710     {
17711         this.picker().show();
17712         this.update();
17713         this.place();
17714         
17715         this.fireEvent('show', this, this.date);
17716     },
17717     
17718     hide : function()
17719     {
17720         if(this.isInline) {
17721             return;
17722         }
17723         this.picker().hide();
17724         this.viewMode = this.startViewMode;
17725         this.showMode();
17726         
17727         this.fireEvent('hide', this, this.date);
17728         
17729     },
17730     
17731     onMousedown: function(e)
17732     {
17733         e.stopPropagation();
17734         e.preventDefault();
17735     },
17736     
17737     keyup: function(e)
17738     {
17739         Roo.bootstrap.DateField.superclass.keyup.call(this);
17740         this.update();
17741     },
17742
17743     setValue: function(v)
17744     {
17745         if(this.fireEvent('beforeselect', this, v) !== false){
17746             var d = new Date(this.parseDate(v) ).clearTime();
17747         
17748             if(isNaN(d.getTime())){
17749                 this.date = this.viewDate = '';
17750                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17751                 return;
17752             }
17753
17754             v = this.formatDate(d);
17755
17756             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17757
17758             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17759
17760             this.update();
17761
17762             this.fireEvent('select', this, this.date);
17763         }
17764     },
17765     
17766     getValue: function()
17767     {
17768         return this.formatDate(this.date);
17769     },
17770     
17771     fireKey: function(e)
17772     {
17773         if (!this.picker().isVisible()){
17774             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17775                 this.show();
17776             }
17777             return;
17778         }
17779         
17780         var dateChanged = false,
17781         dir, day, month,
17782         newDate, newViewDate;
17783         
17784         switch(e.keyCode){
17785             case 27: // escape
17786                 this.hide();
17787                 e.preventDefault();
17788                 break;
17789             case 37: // left
17790             case 39: // right
17791                 if (!this.keyboardNavigation) {
17792                     break;
17793                 }
17794                 dir = e.keyCode == 37 ? -1 : 1;
17795                 
17796                 if (e.ctrlKey){
17797                     newDate = this.moveYear(this.date, dir);
17798                     newViewDate = this.moveYear(this.viewDate, dir);
17799                 } else if (e.shiftKey){
17800                     newDate = this.moveMonth(this.date, dir);
17801                     newViewDate = this.moveMonth(this.viewDate, dir);
17802                 } else {
17803                     newDate = new Date(this.date);
17804                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17805                     newViewDate = new Date(this.viewDate);
17806                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17807                 }
17808                 if (this.dateWithinRange(newDate)){
17809                     this.date = newDate;
17810                     this.viewDate = newViewDate;
17811                     this.setValue(this.formatDate(this.date));
17812 //                    this.update();
17813                     e.preventDefault();
17814                     dateChanged = true;
17815                 }
17816                 break;
17817             case 38: // up
17818             case 40: // down
17819                 if (!this.keyboardNavigation) {
17820                     break;
17821                 }
17822                 dir = e.keyCode == 38 ? -1 : 1;
17823                 if (e.ctrlKey){
17824                     newDate = this.moveYear(this.date, dir);
17825                     newViewDate = this.moveYear(this.viewDate, dir);
17826                 } else if (e.shiftKey){
17827                     newDate = this.moveMonth(this.date, dir);
17828                     newViewDate = this.moveMonth(this.viewDate, dir);
17829                 } else {
17830                     newDate = new Date(this.date);
17831                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17832                     newViewDate = new Date(this.viewDate);
17833                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17834                 }
17835                 if (this.dateWithinRange(newDate)){
17836                     this.date = newDate;
17837                     this.viewDate = newViewDate;
17838                     this.setValue(this.formatDate(this.date));
17839 //                    this.update();
17840                     e.preventDefault();
17841                     dateChanged = true;
17842                 }
17843                 break;
17844             case 13: // enter
17845                 this.setValue(this.formatDate(this.date));
17846                 this.hide();
17847                 e.preventDefault();
17848                 break;
17849             case 9: // tab
17850                 this.setValue(this.formatDate(this.date));
17851                 this.hide();
17852                 break;
17853             case 16: // shift
17854             case 17: // ctrl
17855             case 18: // alt
17856                 break;
17857             default :
17858                 this.hide();
17859                 
17860         }
17861     },
17862     
17863     
17864     onClick: function(e) 
17865     {
17866         e.stopPropagation();
17867         e.preventDefault();
17868         
17869         var target = e.getTarget();
17870         
17871         if(target.nodeName.toLowerCase() === 'i'){
17872             target = Roo.get(target).dom.parentNode;
17873         }
17874         
17875         var nodeName = target.nodeName;
17876         var className = target.className;
17877         var html = target.innerHTML;
17878         //Roo.log(nodeName);
17879         
17880         switch(nodeName.toLowerCase()) {
17881             case 'th':
17882                 switch(className) {
17883                     case 'switch':
17884                         this.showMode(1);
17885                         break;
17886                     case 'prev':
17887                     case 'next':
17888                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17889                         switch(this.viewMode){
17890                                 case 0:
17891                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17892                                         break;
17893                                 case 1:
17894                                 case 2:
17895                                         this.viewDate = this.moveYear(this.viewDate, dir);
17896                                         break;
17897                         }
17898                         this.fill();
17899                         break;
17900                     case 'today':
17901                         var date = new Date();
17902                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17903 //                        this.fill()
17904                         this.setValue(this.formatDate(this.date));
17905                         
17906                         this.hide();
17907                         break;
17908                 }
17909                 break;
17910             case 'span':
17911                 if (className.indexOf('disabled') < 0) {
17912                     this.viewDate.setUTCDate(1);
17913                     if (className.indexOf('month') > -1) {
17914                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17915                     } else {
17916                         var year = parseInt(html, 10) || 0;
17917                         this.viewDate.setUTCFullYear(year);
17918                         
17919                     }
17920                     
17921                     if(this.singleMode){
17922                         this.setValue(this.formatDate(this.viewDate));
17923                         this.hide();
17924                         return;
17925                     }
17926                     
17927                     this.showMode(-1);
17928                     this.fill();
17929                 }
17930                 break;
17931                 
17932             case 'td':
17933                 //Roo.log(className);
17934                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17935                     var day = parseInt(html, 10) || 1;
17936                     var year = this.viewDate.getUTCFullYear(),
17937                         month = this.viewDate.getUTCMonth();
17938
17939                     if (className.indexOf('old') > -1) {
17940                         if(month === 0 ){
17941                             month = 11;
17942                             year -= 1;
17943                         }else{
17944                             month -= 1;
17945                         }
17946                     } else if (className.indexOf('new') > -1) {
17947                         if (month == 11) {
17948                             month = 0;
17949                             year += 1;
17950                         } else {
17951                             month += 1;
17952                         }
17953                     }
17954                     //Roo.log([year,month,day]);
17955                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17956                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17957 //                    this.fill();
17958                     //Roo.log(this.formatDate(this.date));
17959                     this.setValue(this.formatDate(this.date));
17960                     this.hide();
17961                 }
17962                 break;
17963         }
17964     },
17965     
17966     setStartDate: function(startDate)
17967     {
17968         this.startDate = startDate || -Infinity;
17969         if (this.startDate !== -Infinity) {
17970             this.startDate = this.parseDate(this.startDate);
17971         }
17972         this.update();
17973         this.updateNavArrows();
17974     },
17975
17976     setEndDate: function(endDate)
17977     {
17978         this.endDate = endDate || Infinity;
17979         if (this.endDate !== Infinity) {
17980             this.endDate = this.parseDate(this.endDate);
17981         }
17982         this.update();
17983         this.updateNavArrows();
17984     },
17985     
17986     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17987     {
17988         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17989         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17990             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17991         }
17992         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17993             return parseInt(d, 10);
17994         });
17995         this.update();
17996         this.updateNavArrows();
17997     },
17998     
17999     updateNavArrows: function() 
18000     {
18001         if(this.singleMode){
18002             return;
18003         }
18004         
18005         var d = new Date(this.viewDate),
18006         year = d.getUTCFullYear(),
18007         month = d.getUTCMonth();
18008         
18009         Roo.each(this.picker().select('.prev', true).elements, function(v){
18010             v.show();
18011             switch (this.viewMode) {
18012                 case 0:
18013
18014                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18015                         v.hide();
18016                     }
18017                     break;
18018                 case 1:
18019                 case 2:
18020                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18021                         v.hide();
18022                     }
18023                     break;
18024             }
18025         });
18026         
18027         Roo.each(this.picker().select('.next', true).elements, function(v){
18028             v.show();
18029             switch (this.viewMode) {
18030                 case 0:
18031
18032                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18033                         v.hide();
18034                     }
18035                     break;
18036                 case 1:
18037                 case 2:
18038                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18039                         v.hide();
18040                     }
18041                     break;
18042             }
18043         })
18044     },
18045     
18046     moveMonth: function(date, dir)
18047     {
18048         if (!dir) {
18049             return date;
18050         }
18051         var new_date = new Date(date.valueOf()),
18052         day = new_date.getUTCDate(),
18053         month = new_date.getUTCMonth(),
18054         mag = Math.abs(dir),
18055         new_month, test;
18056         dir = dir > 0 ? 1 : -1;
18057         if (mag == 1){
18058             test = dir == -1
18059             // If going back one month, make sure month is not current month
18060             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18061             ? function(){
18062                 return new_date.getUTCMonth() == month;
18063             }
18064             // If going forward one month, make sure month is as expected
18065             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18066             : function(){
18067                 return new_date.getUTCMonth() != new_month;
18068             };
18069             new_month = month + dir;
18070             new_date.setUTCMonth(new_month);
18071             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18072             if (new_month < 0 || new_month > 11) {
18073                 new_month = (new_month + 12) % 12;
18074             }
18075         } else {
18076             // For magnitudes >1, move one month at a time...
18077             for (var i=0; i<mag; i++) {
18078                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18079                 new_date = this.moveMonth(new_date, dir);
18080             }
18081             // ...then reset the day, keeping it in the new month
18082             new_month = new_date.getUTCMonth();
18083             new_date.setUTCDate(day);
18084             test = function(){
18085                 return new_month != new_date.getUTCMonth();
18086             };
18087         }
18088         // Common date-resetting loop -- if date is beyond end of month, make it
18089         // end of month
18090         while (test()){
18091             new_date.setUTCDate(--day);
18092             new_date.setUTCMonth(new_month);
18093         }
18094         return new_date;
18095     },
18096
18097     moveYear: function(date, dir)
18098     {
18099         return this.moveMonth(date, dir*12);
18100     },
18101
18102     dateWithinRange: function(date)
18103     {
18104         return date >= this.startDate && date <= this.endDate;
18105     },
18106
18107     
18108     remove: function() 
18109     {
18110         this.picker().remove();
18111     }
18112    
18113 });
18114
18115 Roo.apply(Roo.bootstrap.DateField,  {
18116     
18117     head : {
18118         tag: 'thead',
18119         cn: [
18120         {
18121             tag: 'tr',
18122             cn: [
18123             {
18124                 tag: 'th',
18125                 cls: 'prev',
18126                 html: '<i class="fa fa-arrow-left"/>'
18127             },
18128             {
18129                 tag: 'th',
18130                 cls: 'switch',
18131                 colspan: '5'
18132             },
18133             {
18134                 tag: 'th',
18135                 cls: 'next',
18136                 html: '<i class="fa fa-arrow-right"/>'
18137             }
18138
18139             ]
18140         }
18141         ]
18142     },
18143     
18144     content : {
18145         tag: 'tbody',
18146         cn: [
18147         {
18148             tag: 'tr',
18149             cn: [
18150             {
18151                 tag: 'td',
18152                 colspan: '7'
18153             }
18154             ]
18155         }
18156         ]
18157     },
18158     
18159     footer : {
18160         tag: 'tfoot',
18161         cn: [
18162         {
18163             tag: 'tr',
18164             cn: [
18165             {
18166                 tag: 'th',
18167                 colspan: '7',
18168                 cls: 'today'
18169             }
18170                     
18171             ]
18172         }
18173         ]
18174     },
18175     
18176     dates:{
18177         en: {
18178             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18179             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18180             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18181             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18182             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18183             today: "Today"
18184         }
18185     },
18186     
18187     modes: [
18188     {
18189         clsName: 'days',
18190         navFnc: 'Month',
18191         navStep: 1
18192     },
18193     {
18194         clsName: 'months',
18195         navFnc: 'FullYear',
18196         navStep: 1
18197     },
18198     {
18199         clsName: 'years',
18200         navFnc: 'FullYear',
18201         navStep: 10
18202     }]
18203 });
18204
18205 Roo.apply(Roo.bootstrap.DateField,  {
18206   
18207     template : {
18208         tag: 'div',
18209         cls: 'datepicker dropdown-menu roo-dynamic',
18210         cn: [
18211         {
18212             tag: 'div',
18213             cls: 'datepicker-days',
18214             cn: [
18215             {
18216                 tag: 'table',
18217                 cls: 'table-condensed',
18218                 cn:[
18219                 Roo.bootstrap.DateField.head,
18220                 {
18221                     tag: 'tbody'
18222                 },
18223                 Roo.bootstrap.DateField.footer
18224                 ]
18225             }
18226             ]
18227         },
18228         {
18229             tag: 'div',
18230             cls: 'datepicker-months',
18231             cn: [
18232             {
18233                 tag: 'table',
18234                 cls: 'table-condensed',
18235                 cn:[
18236                 Roo.bootstrap.DateField.head,
18237                 Roo.bootstrap.DateField.content,
18238                 Roo.bootstrap.DateField.footer
18239                 ]
18240             }
18241             ]
18242         },
18243         {
18244             tag: 'div',
18245             cls: 'datepicker-years',
18246             cn: [
18247             {
18248                 tag: 'table',
18249                 cls: 'table-condensed',
18250                 cn:[
18251                 Roo.bootstrap.DateField.head,
18252                 Roo.bootstrap.DateField.content,
18253                 Roo.bootstrap.DateField.footer
18254                 ]
18255             }
18256             ]
18257         }
18258         ]
18259     }
18260 });
18261
18262  
18263
18264  /*
18265  * - LGPL
18266  *
18267  * TimeField
18268  * 
18269  */
18270
18271 /**
18272  * @class Roo.bootstrap.TimeField
18273  * @extends Roo.bootstrap.Input
18274  * Bootstrap DateField class
18275  * 
18276  * 
18277  * @constructor
18278  * Create a new TimeField
18279  * @param {Object} config The config object
18280  */
18281
18282 Roo.bootstrap.TimeField = function(config){
18283     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18284     this.addEvents({
18285             /**
18286              * @event show
18287              * Fires when this field show.
18288              * @param {Roo.bootstrap.DateField} thisthis
18289              * @param {Mixed} date The date value
18290              */
18291             show : true,
18292             /**
18293              * @event show
18294              * Fires when this field hide.
18295              * @param {Roo.bootstrap.DateField} this
18296              * @param {Mixed} date The date value
18297              */
18298             hide : true,
18299             /**
18300              * @event select
18301              * Fires when select a date.
18302              * @param {Roo.bootstrap.DateField} this
18303              * @param {Mixed} date The date value
18304              */
18305             select : true
18306         });
18307 };
18308
18309 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18310     
18311     /**
18312      * @cfg {String} format
18313      * The default time format string which can be overriden for localization support.  The format must be
18314      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18315      */
18316     format : "H:i",
18317        
18318     onRender: function(ct, position)
18319     {
18320         
18321         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18322                 
18323         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18324         
18325         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18326         
18327         this.pop = this.picker().select('>.datepicker-time',true).first();
18328         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18329         
18330         this.picker().on('mousedown', this.onMousedown, this);
18331         this.picker().on('click', this.onClick, this);
18332         
18333         this.picker().addClass('datepicker-dropdown');
18334     
18335         this.fillTime();
18336         this.update();
18337             
18338         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18339         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18340         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18341         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18342         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18343         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18344
18345     },
18346     
18347     fireKey: function(e){
18348         if (!this.picker().isVisible()){
18349             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18350                 this.show();
18351             }
18352             return;
18353         }
18354
18355         e.preventDefault();
18356         
18357         switch(e.keyCode){
18358             case 27: // escape
18359                 this.hide();
18360                 break;
18361             case 37: // left
18362             case 39: // right
18363                 this.onTogglePeriod();
18364                 break;
18365             case 38: // up
18366                 this.onIncrementMinutes();
18367                 break;
18368             case 40: // down
18369                 this.onDecrementMinutes();
18370                 break;
18371             case 13: // enter
18372             case 9: // tab
18373                 this.setTime();
18374                 break;
18375         }
18376     },
18377     
18378     onClick: function(e) {
18379         e.stopPropagation();
18380         e.preventDefault();
18381     },
18382     
18383     picker : function()
18384     {
18385         return this.el.select('.datepicker', true).first();
18386     },
18387     
18388     fillTime: function()
18389     {    
18390         var time = this.pop.select('tbody', true).first();
18391         
18392         time.dom.innerHTML = '';
18393         
18394         time.createChild({
18395             tag: 'tr',
18396             cn: [
18397                 {
18398                     tag: 'td',
18399                     cn: [
18400                         {
18401                             tag: 'a',
18402                             href: '#',
18403                             cls: 'btn',
18404                             cn: [
18405                                 {
18406                                     tag: 'span',
18407                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18408                                 }
18409                             ]
18410                         } 
18411                     ]
18412                 },
18413                 {
18414                     tag: 'td',
18415                     cls: 'separator'
18416                 },
18417                 {
18418                     tag: 'td',
18419                     cn: [
18420                         {
18421                             tag: 'a',
18422                             href: '#',
18423                             cls: 'btn',
18424                             cn: [
18425                                 {
18426                                     tag: 'span',
18427                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18428                                 }
18429                             ]
18430                         }
18431                     ]
18432                 },
18433                 {
18434                     tag: 'td',
18435                     cls: 'separator'
18436                 }
18437             ]
18438         });
18439         
18440         time.createChild({
18441             tag: 'tr',
18442             cn: [
18443                 {
18444                     tag: 'td',
18445                     cn: [
18446                         {
18447                             tag: 'span',
18448                             cls: 'timepicker-hour',
18449                             html: '00'
18450                         }  
18451                     ]
18452                 },
18453                 {
18454                     tag: 'td',
18455                     cls: 'separator',
18456                     html: ':'
18457                 },
18458                 {
18459                     tag: 'td',
18460                     cn: [
18461                         {
18462                             tag: 'span',
18463                             cls: 'timepicker-minute',
18464                             html: '00'
18465                         }  
18466                     ]
18467                 },
18468                 {
18469                     tag: 'td',
18470                     cls: 'separator'
18471                 },
18472                 {
18473                     tag: 'td',
18474                     cn: [
18475                         {
18476                             tag: 'button',
18477                             type: 'button',
18478                             cls: 'btn btn-primary period',
18479                             html: 'AM'
18480                             
18481                         }
18482                     ]
18483                 }
18484             ]
18485         });
18486         
18487         time.createChild({
18488             tag: 'tr',
18489             cn: [
18490                 {
18491                     tag: 'td',
18492                     cn: [
18493                         {
18494                             tag: 'a',
18495                             href: '#',
18496                             cls: 'btn',
18497                             cn: [
18498                                 {
18499                                     tag: 'span',
18500                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18501                                 }
18502                             ]
18503                         }
18504                     ]
18505                 },
18506                 {
18507                     tag: 'td',
18508                     cls: 'separator'
18509                 },
18510                 {
18511                     tag: 'td',
18512                     cn: [
18513                         {
18514                             tag: 'a',
18515                             href: '#',
18516                             cls: 'btn',
18517                             cn: [
18518                                 {
18519                                     tag: 'span',
18520                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18521                                 }
18522                             ]
18523                         }
18524                     ]
18525                 },
18526                 {
18527                     tag: 'td',
18528                     cls: 'separator'
18529                 }
18530             ]
18531         });
18532         
18533     },
18534     
18535     update: function()
18536     {
18537         
18538         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18539         
18540         this.fill();
18541     },
18542     
18543     fill: function() 
18544     {
18545         var hours = this.time.getHours();
18546         var minutes = this.time.getMinutes();
18547         var period = 'AM';
18548         
18549         if(hours > 11){
18550             period = 'PM';
18551         }
18552         
18553         if(hours == 0){
18554             hours = 12;
18555         }
18556         
18557         
18558         if(hours > 12){
18559             hours = hours - 12;
18560         }
18561         
18562         if(hours < 10){
18563             hours = '0' + hours;
18564         }
18565         
18566         if(minutes < 10){
18567             minutes = '0' + minutes;
18568         }
18569         
18570         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18571         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18572         this.pop.select('button', true).first().dom.innerHTML = period;
18573         
18574     },
18575     
18576     place: function()
18577     {   
18578         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18579         
18580         var cls = ['bottom'];
18581         
18582         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18583             cls.pop();
18584             cls.push('top');
18585         }
18586         
18587         cls.push('right');
18588         
18589         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18590             cls.pop();
18591             cls.push('left');
18592         }
18593         
18594         this.picker().addClass(cls.join('-'));
18595         
18596         var _this = this;
18597         
18598         Roo.each(cls, function(c){
18599             if(c == 'bottom'){
18600                 _this.picker().setTop(_this.inputEl().getHeight());
18601                 return;
18602             }
18603             if(c == 'top'){
18604                 _this.picker().setTop(0 - _this.picker().getHeight());
18605                 return;
18606             }
18607             
18608             if(c == 'left'){
18609                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18610                 return;
18611             }
18612             if(c == 'right'){
18613                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18614                 return;
18615             }
18616         });
18617         
18618     },
18619   
18620     onFocus : function()
18621     {
18622         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18623         this.show();
18624     },
18625     
18626     onBlur : function()
18627     {
18628         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18629         this.hide();
18630     },
18631     
18632     show : function()
18633     {
18634         this.picker().show();
18635         this.pop.show();
18636         this.update();
18637         this.place();
18638         
18639         this.fireEvent('show', this, this.date);
18640     },
18641     
18642     hide : function()
18643     {
18644         this.picker().hide();
18645         this.pop.hide();
18646         
18647         this.fireEvent('hide', this, this.date);
18648     },
18649     
18650     setTime : function()
18651     {
18652         this.hide();
18653         this.setValue(this.time.format(this.format));
18654         
18655         this.fireEvent('select', this, this.date);
18656         
18657         
18658     },
18659     
18660     onMousedown: function(e){
18661         e.stopPropagation();
18662         e.preventDefault();
18663     },
18664     
18665     onIncrementHours: function()
18666     {
18667         Roo.log('onIncrementHours');
18668         this.time = this.time.add(Date.HOUR, 1);
18669         this.update();
18670         
18671     },
18672     
18673     onDecrementHours: function()
18674     {
18675         Roo.log('onDecrementHours');
18676         this.time = this.time.add(Date.HOUR, -1);
18677         this.update();
18678     },
18679     
18680     onIncrementMinutes: function()
18681     {
18682         Roo.log('onIncrementMinutes');
18683         this.time = this.time.add(Date.MINUTE, 1);
18684         this.update();
18685     },
18686     
18687     onDecrementMinutes: function()
18688     {
18689         Roo.log('onDecrementMinutes');
18690         this.time = this.time.add(Date.MINUTE, -1);
18691         this.update();
18692     },
18693     
18694     onTogglePeriod: function()
18695     {
18696         Roo.log('onTogglePeriod');
18697         this.time = this.time.add(Date.HOUR, 12);
18698         this.update();
18699     }
18700     
18701    
18702 });
18703
18704 Roo.apply(Roo.bootstrap.TimeField,  {
18705     
18706     content : {
18707         tag: 'tbody',
18708         cn: [
18709             {
18710                 tag: 'tr',
18711                 cn: [
18712                 {
18713                     tag: 'td',
18714                     colspan: '7'
18715                 }
18716                 ]
18717             }
18718         ]
18719     },
18720     
18721     footer : {
18722         tag: 'tfoot',
18723         cn: [
18724             {
18725                 tag: 'tr',
18726                 cn: [
18727                 {
18728                     tag: 'th',
18729                     colspan: '7',
18730                     cls: '',
18731                     cn: [
18732                         {
18733                             tag: 'button',
18734                             cls: 'btn btn-info ok',
18735                             html: 'OK'
18736                         }
18737                     ]
18738                 }
18739
18740                 ]
18741             }
18742         ]
18743     }
18744 });
18745
18746 Roo.apply(Roo.bootstrap.TimeField,  {
18747   
18748     template : {
18749         tag: 'div',
18750         cls: 'datepicker dropdown-menu',
18751         cn: [
18752             {
18753                 tag: 'div',
18754                 cls: 'datepicker-time',
18755                 cn: [
18756                 {
18757                     tag: 'table',
18758                     cls: 'table-condensed',
18759                     cn:[
18760                     Roo.bootstrap.TimeField.content,
18761                     Roo.bootstrap.TimeField.footer
18762                     ]
18763                 }
18764                 ]
18765             }
18766         ]
18767     }
18768 });
18769
18770  
18771
18772  /*
18773  * - LGPL
18774  *
18775  * MonthField
18776  * 
18777  */
18778
18779 /**
18780  * @class Roo.bootstrap.MonthField
18781  * @extends Roo.bootstrap.Input
18782  * Bootstrap MonthField class
18783  * 
18784  * @cfg {String} language default en
18785  * 
18786  * @constructor
18787  * Create a new MonthField
18788  * @param {Object} config The config object
18789  */
18790
18791 Roo.bootstrap.MonthField = function(config){
18792     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18793     
18794     this.addEvents({
18795         /**
18796          * @event show
18797          * Fires when this field show.
18798          * @param {Roo.bootstrap.MonthField} this
18799          * @param {Mixed} date The date value
18800          */
18801         show : true,
18802         /**
18803          * @event show
18804          * Fires when this field hide.
18805          * @param {Roo.bootstrap.MonthField} this
18806          * @param {Mixed} date The date value
18807          */
18808         hide : true,
18809         /**
18810          * @event select
18811          * Fires when select a date.
18812          * @param {Roo.bootstrap.MonthField} this
18813          * @param {String} oldvalue The old value
18814          * @param {String} newvalue The new value
18815          */
18816         select : true
18817     });
18818 };
18819
18820 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18821     
18822     onRender: function(ct, position)
18823     {
18824         
18825         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18826         
18827         this.language = this.language || 'en';
18828         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18829         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18830         
18831         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18832         this.isInline = false;
18833         this.isInput = true;
18834         this.component = this.el.select('.add-on', true).first() || false;
18835         this.component = (this.component && this.component.length === 0) ? false : this.component;
18836         this.hasInput = this.component && this.inputEL().length;
18837         
18838         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18839         
18840         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18841         
18842         this.picker().on('mousedown', this.onMousedown, this);
18843         this.picker().on('click', this.onClick, this);
18844         
18845         this.picker().addClass('datepicker-dropdown');
18846         
18847         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18848             v.setStyle('width', '189px');
18849         });
18850         
18851         this.fillMonths();
18852         
18853         this.update();
18854         
18855         if(this.isInline) {
18856             this.show();
18857         }
18858         
18859     },
18860     
18861     setValue: function(v, suppressEvent)
18862     {   
18863         var o = this.getValue();
18864         
18865         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18866         
18867         this.update();
18868
18869         if(suppressEvent !== true){
18870             this.fireEvent('select', this, o, v);
18871         }
18872         
18873     },
18874     
18875     getValue: function()
18876     {
18877         return this.value;
18878     },
18879     
18880     onClick: function(e) 
18881     {
18882         e.stopPropagation();
18883         e.preventDefault();
18884         
18885         var target = e.getTarget();
18886         
18887         if(target.nodeName.toLowerCase() === 'i'){
18888             target = Roo.get(target).dom.parentNode;
18889         }
18890         
18891         var nodeName = target.nodeName;
18892         var className = target.className;
18893         var html = target.innerHTML;
18894         
18895         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18896             return;
18897         }
18898         
18899         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18900         
18901         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18902         
18903         this.hide();
18904                         
18905     },
18906     
18907     picker : function()
18908     {
18909         return this.pickerEl;
18910     },
18911     
18912     fillMonths: function()
18913     {    
18914         var i = 0;
18915         var months = this.picker().select('>.datepicker-months td', true).first();
18916         
18917         months.dom.innerHTML = '';
18918         
18919         while (i < 12) {
18920             var month = {
18921                 tag: 'span',
18922                 cls: 'month',
18923                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18924             };
18925             
18926             months.createChild(month);
18927         }
18928         
18929     },
18930     
18931     update: function()
18932     {
18933         var _this = this;
18934         
18935         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18936             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18937         }
18938         
18939         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18940             e.removeClass('active');
18941             
18942             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18943                 e.addClass('active');
18944             }
18945         })
18946     },
18947     
18948     place: function()
18949     {
18950         if(this.isInline) {
18951             return;
18952         }
18953         
18954         this.picker().removeClass(['bottom', 'top']);
18955         
18956         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18957             /*
18958              * place to the top of element!
18959              *
18960              */
18961             
18962             this.picker().addClass('top');
18963             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18964             
18965             return;
18966         }
18967         
18968         this.picker().addClass('bottom');
18969         
18970         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18971     },
18972     
18973     onFocus : function()
18974     {
18975         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18976         this.show();
18977     },
18978     
18979     onBlur : function()
18980     {
18981         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18982         
18983         var d = this.inputEl().getValue();
18984         
18985         this.setValue(d);
18986                 
18987         this.hide();
18988     },
18989     
18990     show : function()
18991     {
18992         this.picker().show();
18993         this.picker().select('>.datepicker-months', true).first().show();
18994         this.update();
18995         this.place();
18996         
18997         this.fireEvent('show', this, this.date);
18998     },
18999     
19000     hide : function()
19001     {
19002         if(this.isInline) {
19003             return;
19004         }
19005         this.picker().hide();
19006         this.fireEvent('hide', this, this.date);
19007         
19008     },
19009     
19010     onMousedown: function(e)
19011     {
19012         e.stopPropagation();
19013         e.preventDefault();
19014     },
19015     
19016     keyup: function(e)
19017     {
19018         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19019         this.update();
19020     },
19021
19022     fireKey: function(e)
19023     {
19024         if (!this.picker().isVisible()){
19025             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19026                 this.show();
19027             }
19028             return;
19029         }
19030         
19031         var dir;
19032         
19033         switch(e.keyCode){
19034             case 27: // escape
19035                 this.hide();
19036                 e.preventDefault();
19037                 break;
19038             case 37: // left
19039             case 39: // right
19040                 dir = e.keyCode == 37 ? -1 : 1;
19041                 
19042                 this.vIndex = this.vIndex + dir;
19043                 
19044                 if(this.vIndex < 0){
19045                     this.vIndex = 0;
19046                 }
19047                 
19048                 if(this.vIndex > 11){
19049                     this.vIndex = 11;
19050                 }
19051                 
19052                 if(isNaN(this.vIndex)){
19053                     this.vIndex = 0;
19054                 }
19055                 
19056                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19057                 
19058                 break;
19059             case 38: // up
19060             case 40: // down
19061                 
19062                 dir = e.keyCode == 38 ? -1 : 1;
19063                 
19064                 this.vIndex = this.vIndex + dir * 4;
19065                 
19066                 if(this.vIndex < 0){
19067                     this.vIndex = 0;
19068                 }
19069                 
19070                 if(this.vIndex > 11){
19071                     this.vIndex = 11;
19072                 }
19073                 
19074                 if(isNaN(this.vIndex)){
19075                     this.vIndex = 0;
19076                 }
19077                 
19078                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19079                 break;
19080                 
19081             case 13: // enter
19082                 
19083                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19084                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19085                 }
19086                 
19087                 this.hide();
19088                 e.preventDefault();
19089                 break;
19090             case 9: // tab
19091                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19092                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19093                 }
19094                 this.hide();
19095                 break;
19096             case 16: // shift
19097             case 17: // ctrl
19098             case 18: // alt
19099                 break;
19100             default :
19101                 this.hide();
19102                 
19103         }
19104     },
19105     
19106     remove: function() 
19107     {
19108         this.picker().remove();
19109     }
19110    
19111 });
19112
19113 Roo.apply(Roo.bootstrap.MonthField,  {
19114     
19115     content : {
19116         tag: 'tbody',
19117         cn: [
19118         {
19119             tag: 'tr',
19120             cn: [
19121             {
19122                 tag: 'td',
19123                 colspan: '7'
19124             }
19125             ]
19126         }
19127         ]
19128     },
19129     
19130     dates:{
19131         en: {
19132             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19133             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19134         }
19135     }
19136 });
19137
19138 Roo.apply(Roo.bootstrap.MonthField,  {
19139   
19140     template : {
19141         tag: 'div',
19142         cls: 'datepicker dropdown-menu roo-dynamic',
19143         cn: [
19144             {
19145                 tag: 'div',
19146                 cls: 'datepicker-months',
19147                 cn: [
19148                 {
19149                     tag: 'table',
19150                     cls: 'table-condensed',
19151                     cn:[
19152                         Roo.bootstrap.DateField.content
19153                     ]
19154                 }
19155                 ]
19156             }
19157         ]
19158     }
19159 });
19160
19161  
19162
19163  
19164  /*
19165  * - LGPL
19166  *
19167  * CheckBox
19168  * 
19169  */
19170
19171 /**
19172  * @class Roo.bootstrap.CheckBox
19173  * @extends Roo.bootstrap.Input
19174  * Bootstrap CheckBox class
19175  * 
19176  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19177  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19178  * @cfg {String} boxLabel The text that appears beside the checkbox
19179  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19180  * @cfg {Boolean} checked initnal the element
19181  * @cfg {Boolean} inline inline the element (default false)
19182  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19183  * 
19184  * @constructor
19185  * Create a new CheckBox
19186  * @param {Object} config The config object
19187  */
19188
19189 Roo.bootstrap.CheckBox = function(config){
19190     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19191    
19192     this.addEvents({
19193         /**
19194         * @event check
19195         * Fires when the element is checked or unchecked.
19196         * @param {Roo.bootstrap.CheckBox} this This input
19197         * @param {Boolean} checked The new checked value
19198         */
19199        check : true
19200     });
19201     
19202 };
19203
19204 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19205   
19206     inputType: 'checkbox',
19207     inputValue: 1,
19208     valueOff: 0,
19209     boxLabel: false,
19210     checked: false,
19211     weight : false,
19212     inline: false,
19213     
19214     getAutoCreate : function()
19215     {
19216         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19217         
19218         var id = Roo.id();
19219         
19220         var cfg = {};
19221         
19222         cfg.cls = 'form-group ' + this.inputType; //input-group
19223         
19224         if(this.inline){
19225             cfg.cls += ' ' + this.inputType + '-inline';
19226         }
19227         
19228         var input =  {
19229             tag: 'input',
19230             id : id,
19231             type : this.inputType,
19232             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19233             cls : 'roo-' + this.inputType, //'form-box',
19234             placeholder : this.placeholder || ''
19235             
19236         };
19237         
19238         if (this.weight) { // Validity check?
19239             cfg.cls += " " + this.inputType + "-" + this.weight;
19240         }
19241         
19242         if (this.disabled) {
19243             input.disabled=true;
19244         }
19245         
19246         if(this.checked){
19247             input.checked = this.checked;
19248         }
19249         
19250         if (this.name) {
19251             input.name = this.name;
19252         }
19253         
19254         if (this.size) {
19255             input.cls += ' input-' + this.size;
19256         }
19257         
19258         var settings=this;
19259         
19260         ['xs','sm','md','lg'].map(function(size){
19261             if (settings[size]) {
19262                 cfg.cls += ' col-' + size + '-' + settings[size];
19263             }
19264         });
19265         
19266         var inputblock = input;
19267          
19268         if (this.before || this.after) {
19269             
19270             inputblock = {
19271                 cls : 'input-group',
19272                 cn :  [] 
19273             };
19274             
19275             if (this.before) {
19276                 inputblock.cn.push({
19277                     tag :'span',
19278                     cls : 'input-group-addon',
19279                     html : this.before
19280                 });
19281             }
19282             
19283             inputblock.cn.push(input);
19284             
19285             if (this.after) {
19286                 inputblock.cn.push({
19287                     tag :'span',
19288                     cls : 'input-group-addon',
19289                     html : this.after
19290                 });
19291             }
19292             
19293         }
19294         
19295         if (align ==='left' && this.fieldLabel.length) {
19296 //                Roo.log("left and has label");
19297                 cfg.cn = [
19298                     
19299                     {
19300                         tag: 'label',
19301                         'for' :  id,
19302                         cls : 'control-label col-md-' + this.labelWidth,
19303                         html : this.fieldLabel
19304                         
19305                     },
19306                     {
19307                         cls : "col-md-" + (12 - this.labelWidth), 
19308                         cn: [
19309                             inputblock
19310                         ]
19311                     }
19312                     
19313                 ];
19314         } else if ( this.fieldLabel.length) {
19315 //                Roo.log(" label");
19316                 cfg.cn = [
19317                    
19318                     {
19319                         tag: this.boxLabel ? 'span' : 'label',
19320                         'for': id,
19321                         cls: 'control-label box-input-label',
19322                         //cls : 'input-group-addon',
19323                         html : this.fieldLabel
19324                         
19325                     },
19326                     
19327                     inputblock
19328                     
19329                 ];
19330
19331         } else {
19332             
19333 //                Roo.log(" no label && no align");
19334                 cfg.cn = [  inputblock ] ;
19335                 
19336                 
19337         }
19338         
19339         if(this.boxLabel){
19340              var boxLabelCfg = {
19341                 tag: 'label',
19342                 //'for': id, // box label is handled by onclick - so no for...
19343                 cls: 'box-label',
19344                 html: this.boxLabel
19345             };
19346             
19347             if(this.tooltip){
19348                 boxLabelCfg.tooltip = this.tooltip;
19349             }
19350              
19351             cfg.cn.push(boxLabelCfg);
19352         }
19353         
19354         
19355        
19356         return cfg;
19357         
19358     },
19359     
19360     /**
19361      * return the real input element.
19362      */
19363     inputEl: function ()
19364     {
19365         return this.el.select('input.roo-' + this.inputType,true).first();
19366     },
19367     
19368     labelEl: function()
19369     {
19370         return this.el.select('label.control-label',true).first();
19371     },
19372     /* depricated... */
19373     
19374     label: function()
19375     {
19376         return this.labelEl();
19377     },
19378     
19379     boxLabelEl: function()
19380     {
19381         return this.el.select('label.box-label',true).first();
19382     },
19383     
19384     initEvents : function()
19385     {
19386 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19387         
19388         this.inputEl().on('click', this.onClick,  this);
19389         
19390         if (this.boxLabel) { 
19391             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19392         }
19393         
19394         this.startValue = this.getValue();
19395         
19396         if(this.groupId){
19397             Roo.bootstrap.CheckBox.register(this);
19398         }
19399     },
19400     
19401     onClick : function()
19402     {   
19403         this.setChecked(!this.checked);
19404     },
19405     
19406     setChecked : function(state,suppressEvent)
19407     {
19408         this.startValue = this.getValue();
19409         
19410         if(this.inputType == 'radio'){
19411             
19412             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19413                 e.dom.checked = false;
19414             });
19415             
19416             this.inputEl().dom.checked = true;
19417             
19418             this.inputEl().dom.value = this.inputValue;
19419             
19420             if(suppressEvent !== true){
19421                 this.fireEvent('check', this, true);
19422             }
19423             
19424             this.validate();
19425             
19426             return;
19427         }
19428         
19429         this.checked = state;
19430         
19431         this.inputEl().dom.checked = state;
19432         
19433         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19434         
19435         if(suppressEvent !== true){
19436             this.fireEvent('check', this, state);
19437         }
19438         
19439         this.validate();
19440     },
19441     
19442     getValue : function()
19443     {
19444         if(this.inputType == 'radio'){
19445             return this.getGroupValue();
19446         }
19447         
19448         return this.inputEl().getValue();
19449         
19450     },
19451     
19452     getGroupValue : function()
19453     {
19454         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19455             return '';
19456         }
19457         
19458         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19459     },
19460     
19461     setValue : function(v,suppressEvent)
19462     {
19463         if(this.inputType == 'radio'){
19464             this.setGroupValue(v, suppressEvent);
19465             return;
19466         }
19467         
19468         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19469         
19470         this.validate();
19471     },
19472     
19473     setGroupValue : function(v, suppressEvent)
19474     {
19475         this.startValue = this.getValue();
19476         
19477         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19478             e.dom.checked = false;
19479             
19480             if(e.dom.value == v){
19481                 e.dom.checked = true;
19482             }
19483         });
19484         
19485         if(suppressEvent !== true){
19486             this.fireEvent('check', this, true);
19487         }
19488
19489         this.validate();
19490         
19491         return;
19492     },
19493     
19494     validate : function()
19495     {
19496         if(
19497                 this.disabled || 
19498                 (this.inputType == 'radio' && this.validateRadio()) ||
19499                 (this.inputType == 'checkbox' && this.validateCheckbox())
19500         ){
19501             this.markValid();
19502             return true;
19503         }
19504         
19505         this.markInvalid();
19506         return false;
19507     },
19508     
19509     validateRadio : function()
19510     {
19511         var valid = false;
19512         
19513         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19514             if(!e.dom.checked){
19515                 return;
19516             }
19517             
19518             valid = true;
19519             
19520             return false;
19521         });
19522         
19523         return valid;
19524     },
19525     
19526     validateCheckbox : function()
19527     {
19528         if(!this.groupId){
19529             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19530         }
19531         
19532         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19533         
19534         if(!group){
19535             return false;
19536         }
19537         
19538         var r = false;
19539         
19540         for(var i in group){
19541             if(r){
19542                 break;
19543             }
19544             
19545             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19546         }
19547         
19548         return r;
19549     },
19550     
19551     /**
19552      * Mark this field as valid
19553      */
19554     markValid : function()
19555     {
19556         if(this.allowBlank){
19557             return;
19558         }
19559         
19560         var _this = this;
19561         
19562         this.fireEvent('valid', this);
19563         
19564         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19565         
19566         if(this.groupId){
19567             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19568         }
19569         
19570         if(label){
19571             label.markValid();
19572         }
19573         
19574         if(this.inputType == 'radio'){
19575             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19576                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19577                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19578             });
19579             
19580             return;
19581         }
19582         
19583         if(!this.groupId){
19584             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19585             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19586             return;
19587         }
19588         
19589         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19590             
19591         if(!group){
19592             return;
19593         }
19594         
19595         for(var i in group){
19596             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19597             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19598         }
19599     },
19600     
19601      /**
19602      * Mark this field as invalid
19603      * @param {String} msg The validation message
19604      */
19605     markInvalid : function(msg)
19606     {
19607         if(this.allowBlank){
19608             return;
19609         }
19610         
19611         var _this = this;
19612         
19613         this.fireEvent('invalid', this, msg);
19614         
19615         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19616         
19617         if(this.groupId){
19618             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19619         }
19620         
19621         if(label){
19622             label.markInvalid();
19623         }
19624             
19625         if(this.inputType == 'radio'){
19626             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19627                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19628                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19629             });
19630             
19631             return;
19632         }
19633         
19634         if(!this.groupId){
19635             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19636             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19637             return;
19638         }
19639         
19640         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19641         
19642         if(!group){
19643             return;
19644         }
19645         
19646         for(var i in group){
19647             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19648             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19649         }
19650         
19651     }
19652     
19653 });
19654
19655 Roo.apply(Roo.bootstrap.CheckBox, {
19656     
19657     groups: {},
19658     
19659      /**
19660     * register a CheckBox Group
19661     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19662     */
19663     register : function(checkbox)
19664     {
19665         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19666             this.groups[checkbox.groupId] = {};
19667         }
19668         
19669         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19670             return;
19671         }
19672         
19673         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19674         
19675     },
19676     /**
19677     * fetch a CheckBox Group based on the group ID
19678     * @param {string} the group ID
19679     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19680     */
19681     get: function(groupId) {
19682         if (typeof(this.groups[groupId]) == 'undefined') {
19683             return false;
19684         }
19685         
19686         return this.groups[groupId] ;
19687     }
19688     
19689     
19690 });
19691 /*
19692  * - LGPL
19693  *
19694  * Radio
19695  *
19696  *
19697  * not inline
19698  *<div class="radio">
19699   <label>
19700     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19701     Option one is this and that&mdash;be sure to include why it's great
19702   </label>
19703 </div>
19704  *
19705  *
19706  *inline
19707  *<span>
19708  *<label class="radio-inline">fieldLabel</label>
19709  *<label class="radio-inline">
19710   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19711 </label>
19712 <span>
19713  * 
19714  * 
19715  */
19716
19717 /**
19718  * @class Roo.bootstrap.Radio
19719  * @extends Roo.bootstrap.CheckBox
19720  * Bootstrap Radio class
19721
19722  * @constructor
19723  * Create a new Radio
19724  * @param {Object} config The config object
19725  */
19726
19727 Roo.bootstrap.Radio = function(config){
19728     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19729    
19730 };
19731
19732 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19733     
19734     inputType: 'radio',
19735     inputValue: '',
19736     valueOff: '',
19737     
19738     getAutoCreate : function()
19739     {
19740         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19741         align = align || 'left'; // default...
19742         
19743         
19744         
19745         var id = Roo.id();
19746         
19747         var cfg = {
19748                 tag : this.inline ? 'span' : 'div',
19749                 cls : '',
19750                 cn : []
19751         };
19752         
19753         var inline = this.inline ? ' radio-inline' : '';
19754         
19755         var lbl = {
19756                 tag: 'label' ,
19757                 // does not need for, as we wrap the input with it..
19758                 'for' : id,
19759                 cls : 'control-label box-label' + inline,
19760                 cn : []
19761         };
19762         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19763         
19764         var fieldLabel = {
19765             tag: 'label' ,
19766             //cls : 'control-label' + inline,
19767             html : this.fieldLabel,
19768             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19769         };
19770         
19771  
19772         
19773         
19774         var input =  {
19775             tag: 'input',
19776             id : id,
19777             type : this.inputType,
19778             //value : (!this.checked) ? this.valueOff : this.inputValue,
19779             value : this.inputValue,
19780             cls : 'roo-radio',
19781             placeholder : this.placeholder || '' // ?? needed????
19782             
19783         };
19784         if (this.weight) { // Validity check?
19785             input.cls += " radio-" + this.weight;
19786         }
19787         if (this.disabled) {
19788             input.disabled=true;
19789         }
19790         
19791         if(this.checked){
19792             input.checked = this.checked;
19793         }
19794         
19795         if (this.name) {
19796             input.name = this.name;
19797         }
19798         
19799         if (this.size) {
19800             input.cls += ' input-' + this.size;
19801         }
19802         
19803         //?? can span's inline have a width??
19804         
19805         var settings=this;
19806         ['xs','sm','md','lg'].map(function(size){
19807             if (settings[size]) {
19808                 cfg.cls += ' col-' + size + '-' + settings[size];
19809             }
19810         });
19811         
19812         var inputblock = input;
19813         
19814         if (this.before || this.after) {
19815             
19816             inputblock = {
19817                 cls : 'input-group',
19818                 tag : 'span',
19819                 cn :  [] 
19820             };
19821             if (this.before) {
19822                 inputblock.cn.push({
19823                     tag :'span',
19824                     cls : 'input-group-addon',
19825                     html : this.before
19826                 });
19827             }
19828             inputblock.cn.push(input);
19829             if (this.after) {
19830                 inputblock.cn.push({
19831                     tag :'span',
19832                     cls : 'input-group-addon',
19833                     html : this.after
19834                 });
19835             }
19836             
19837         };
19838         
19839         
19840         if (this.fieldLabel && this.fieldLabel.length) {
19841             cfg.cn.push(fieldLabel);
19842         }
19843        
19844         // normal bootstrap puts the input inside the label.
19845         // however with our styled version - it has to go after the input.
19846        
19847         //lbl.cn.push(inputblock);
19848         
19849         var lblwrap =  {
19850             tag: 'span',
19851             cls: 'radio' + inline,
19852             cn: [
19853                 inputblock,
19854                 lbl
19855             ]
19856         };
19857         
19858         cfg.cn.push( lblwrap);
19859         
19860         if(this.boxLabel){
19861             lbl.cn.push({
19862                 tag: 'span',
19863                 html: this.boxLabel
19864             })
19865         }
19866          
19867         
19868         return cfg;
19869         
19870     },
19871     
19872     initEvents : function()
19873     {
19874 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19875         
19876         this.inputEl().on('click', this.onClick,  this);
19877         if (this.boxLabel) {
19878             //Roo.log('find label');
19879             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19880         }
19881         
19882     },
19883     
19884     inputEl: function ()
19885     {
19886         return this.el.select('input.roo-radio',true).first();
19887     },
19888     onClick : function()
19889     {   
19890         Roo.log("click");
19891         this.setChecked(true);
19892     },
19893     
19894     setChecked : function(state,suppressEvent)
19895     {
19896         if(state){
19897             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19898                 v.dom.checked = false;
19899             });
19900         }
19901         Roo.log(this.inputEl().dom);
19902         this.checked = state;
19903         this.inputEl().dom.checked = state;
19904         
19905         if(suppressEvent !== true){
19906             this.fireEvent('check', this, state);
19907         }
19908         
19909         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19910         
19911     },
19912     
19913     getGroupValue : function()
19914     {
19915         var value = '';
19916         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19917             if(v.dom.checked == true){
19918                 value = v.dom.value;
19919             }
19920         });
19921         
19922         return value;
19923     },
19924     
19925     /**
19926      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19927      * @return {Mixed} value The field value
19928      */
19929     getValue : function(){
19930         return this.getGroupValue();
19931     }
19932     
19933 });
19934
19935  
19936 //<script type="text/javascript">
19937
19938 /*
19939  * Based  Ext JS Library 1.1.1
19940  * Copyright(c) 2006-2007, Ext JS, LLC.
19941  * LGPL
19942  *
19943  */
19944  
19945 /**
19946  * @class Roo.HtmlEditorCore
19947  * @extends Roo.Component
19948  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19949  *
19950  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19951  */
19952
19953 Roo.HtmlEditorCore = function(config){
19954     
19955     
19956     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19957     
19958     
19959     this.addEvents({
19960         /**
19961          * @event initialize
19962          * Fires when the editor is fully initialized (including the iframe)
19963          * @param {Roo.HtmlEditorCore} this
19964          */
19965         initialize: true,
19966         /**
19967          * @event activate
19968          * Fires when the editor is first receives the focus. Any insertion must wait
19969          * until after this event.
19970          * @param {Roo.HtmlEditorCore} this
19971          */
19972         activate: true,
19973          /**
19974          * @event beforesync
19975          * Fires before the textarea is updated with content from the editor iframe. Return false
19976          * to cancel the sync.
19977          * @param {Roo.HtmlEditorCore} this
19978          * @param {String} html
19979          */
19980         beforesync: true,
19981          /**
19982          * @event beforepush
19983          * Fires before the iframe editor is updated with content from the textarea. Return false
19984          * to cancel the push.
19985          * @param {Roo.HtmlEditorCore} this
19986          * @param {String} html
19987          */
19988         beforepush: true,
19989          /**
19990          * @event sync
19991          * Fires when the textarea is updated with content from the editor iframe.
19992          * @param {Roo.HtmlEditorCore} this
19993          * @param {String} html
19994          */
19995         sync: true,
19996          /**
19997          * @event push
19998          * Fires when the iframe editor is updated with content from the textarea.
19999          * @param {Roo.HtmlEditorCore} this
20000          * @param {String} html
20001          */
20002         push: true,
20003         
20004         /**
20005          * @event editorevent
20006          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20007          * @param {Roo.HtmlEditorCore} this
20008          */
20009         editorevent: true
20010         
20011     });
20012     
20013     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20014     
20015     // defaults : white / black...
20016     this.applyBlacklists();
20017     
20018     
20019     
20020 };
20021
20022
20023 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20024
20025
20026      /**
20027      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20028      */
20029     
20030     owner : false,
20031     
20032      /**
20033      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20034      *                        Roo.resizable.
20035      */
20036     resizable : false,
20037      /**
20038      * @cfg {Number} height (in pixels)
20039      */   
20040     height: 300,
20041    /**
20042      * @cfg {Number} width (in pixels)
20043      */   
20044     width: 500,
20045     
20046     /**
20047      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20048      * 
20049      */
20050     stylesheets: false,
20051     
20052     // id of frame..
20053     frameId: false,
20054     
20055     // private properties
20056     validationEvent : false,
20057     deferHeight: true,
20058     initialized : false,
20059     activated : false,
20060     sourceEditMode : false,
20061     onFocus : Roo.emptyFn,
20062     iframePad:3,
20063     hideMode:'offsets',
20064     
20065     clearUp: true,
20066     
20067     // blacklist + whitelisted elements..
20068     black: false,
20069     white: false,
20070      
20071     
20072
20073     /**
20074      * Protected method that will not generally be called directly. It
20075      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20076      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20077      */
20078     getDocMarkup : function(){
20079         // body styles..
20080         var st = '';
20081         
20082         // inherit styels from page...?? 
20083         if (this.stylesheets === false) {
20084             
20085             Roo.get(document.head).select('style').each(function(node) {
20086                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20087             });
20088             
20089             Roo.get(document.head).select('link').each(function(node) { 
20090                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20091             });
20092             
20093         } else if (!this.stylesheets.length) {
20094                 // simple..
20095                 st = '<style type="text/css">' +
20096                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20097                    '</style>';
20098         } else { 
20099             
20100         }
20101         
20102         st +=  '<style type="text/css">' +
20103             'IMG { cursor: pointer } ' +
20104         '</style>';
20105
20106         
20107         return '<html><head>' + st  +
20108             //<style type="text/css">' +
20109             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20110             //'</style>' +
20111             ' </head><body class="roo-htmleditor-body"></body></html>';
20112     },
20113
20114     // private
20115     onRender : function(ct, position)
20116     {
20117         var _t = this;
20118         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20119         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20120         
20121         
20122         this.el.dom.style.border = '0 none';
20123         this.el.dom.setAttribute('tabIndex', -1);
20124         this.el.addClass('x-hidden hide');
20125         
20126         
20127         
20128         if(Roo.isIE){ // fix IE 1px bogus margin
20129             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20130         }
20131        
20132         
20133         this.frameId = Roo.id();
20134         
20135          
20136         
20137         var iframe = this.owner.wrap.createChild({
20138             tag: 'iframe',
20139             cls: 'form-control', // bootstrap..
20140             id: this.frameId,
20141             name: this.frameId,
20142             frameBorder : 'no',
20143             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20144         }, this.el
20145         );
20146         
20147         
20148         this.iframe = iframe.dom;
20149
20150          this.assignDocWin();
20151         
20152         this.doc.designMode = 'on';
20153        
20154         this.doc.open();
20155         this.doc.write(this.getDocMarkup());
20156         this.doc.close();
20157
20158         
20159         var task = { // must defer to wait for browser to be ready
20160             run : function(){
20161                 //console.log("run task?" + this.doc.readyState);
20162                 this.assignDocWin();
20163                 if(this.doc.body || this.doc.readyState == 'complete'){
20164                     try {
20165                         this.doc.designMode="on";
20166                     } catch (e) {
20167                         return;
20168                     }
20169                     Roo.TaskMgr.stop(task);
20170                     this.initEditor.defer(10, this);
20171                 }
20172             },
20173             interval : 10,
20174             duration: 10000,
20175             scope: this
20176         };
20177         Roo.TaskMgr.start(task);
20178
20179     },
20180
20181     // private
20182     onResize : function(w, h)
20183     {
20184          Roo.log('resize: ' +w + ',' + h );
20185         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20186         if(!this.iframe){
20187             return;
20188         }
20189         if(typeof w == 'number'){
20190             
20191             this.iframe.style.width = w + 'px';
20192         }
20193         if(typeof h == 'number'){
20194             
20195             this.iframe.style.height = h + 'px';
20196             if(this.doc){
20197                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20198             }
20199         }
20200         
20201     },
20202
20203     /**
20204      * Toggles the editor between standard and source edit mode.
20205      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20206      */
20207     toggleSourceEdit : function(sourceEditMode){
20208         
20209         this.sourceEditMode = sourceEditMode === true;
20210         
20211         if(this.sourceEditMode){
20212  
20213             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20214             
20215         }else{
20216             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20217             //this.iframe.className = '';
20218             this.deferFocus();
20219         }
20220         //this.setSize(this.owner.wrap.getSize());
20221         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20222     },
20223
20224     
20225   
20226
20227     /**
20228      * Protected method that will not generally be called directly. If you need/want
20229      * custom HTML cleanup, this is the method you should override.
20230      * @param {String} html The HTML to be cleaned
20231      * return {String} The cleaned HTML
20232      */
20233     cleanHtml : function(html){
20234         html = String(html);
20235         if(html.length > 5){
20236             if(Roo.isSafari){ // strip safari nonsense
20237                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20238             }
20239         }
20240         if(html == '&nbsp;'){
20241             html = '';
20242         }
20243         return html;
20244     },
20245
20246     /**
20247      * HTML Editor -> Textarea
20248      * Protected method that will not generally be called directly. Syncs the contents
20249      * of the editor iframe with the textarea.
20250      */
20251     syncValue : function(){
20252         if(this.initialized){
20253             var bd = (this.doc.body || this.doc.documentElement);
20254             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20255             var html = bd.innerHTML;
20256             if(Roo.isSafari){
20257                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20258                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20259                 if(m && m[1]){
20260                     html = '<div style="'+m[0]+'">' + html + '</div>';
20261                 }
20262             }
20263             html = this.cleanHtml(html);
20264             // fix up the special chars.. normaly like back quotes in word...
20265             // however we do not want to do this with chinese..
20266             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20267                 var cc = b.charCodeAt();
20268                 if (
20269                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20270                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20271                     (cc >= 0xf900 && cc < 0xfb00 )
20272                 ) {
20273                         return b;
20274                 }
20275                 return "&#"+cc+";" 
20276             });
20277             if(this.owner.fireEvent('beforesync', this, html) !== false){
20278                 this.el.dom.value = html;
20279                 this.owner.fireEvent('sync', this, html);
20280             }
20281         }
20282     },
20283
20284     /**
20285      * Protected method that will not generally be called directly. Pushes the value of the textarea
20286      * into the iframe editor.
20287      */
20288     pushValue : function(){
20289         if(this.initialized){
20290             var v = this.el.dom.value.trim();
20291             
20292 //            if(v.length < 1){
20293 //                v = '&#160;';
20294 //            }
20295             
20296             if(this.owner.fireEvent('beforepush', this, v) !== false){
20297                 var d = (this.doc.body || this.doc.documentElement);
20298                 d.innerHTML = v;
20299                 this.cleanUpPaste();
20300                 this.el.dom.value = d.innerHTML;
20301                 this.owner.fireEvent('push', this, v);
20302             }
20303         }
20304     },
20305
20306     // private
20307     deferFocus : function(){
20308         this.focus.defer(10, this);
20309     },
20310
20311     // doc'ed in Field
20312     focus : function(){
20313         if(this.win && !this.sourceEditMode){
20314             this.win.focus();
20315         }else{
20316             this.el.focus();
20317         }
20318     },
20319     
20320     assignDocWin: function()
20321     {
20322         var iframe = this.iframe;
20323         
20324          if(Roo.isIE){
20325             this.doc = iframe.contentWindow.document;
20326             this.win = iframe.contentWindow;
20327         } else {
20328 //            if (!Roo.get(this.frameId)) {
20329 //                return;
20330 //            }
20331 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20332 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20333             
20334             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20335                 return;
20336             }
20337             
20338             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20339             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20340         }
20341     },
20342     
20343     // private
20344     initEditor : function(){
20345         //console.log("INIT EDITOR");
20346         this.assignDocWin();
20347         
20348         
20349         
20350         this.doc.designMode="on";
20351         this.doc.open();
20352         this.doc.write(this.getDocMarkup());
20353         this.doc.close();
20354         
20355         var dbody = (this.doc.body || this.doc.documentElement);
20356         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20357         // this copies styles from the containing element into thsi one..
20358         // not sure why we need all of this..
20359         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20360         
20361         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20362         //ss['background-attachment'] = 'fixed'; // w3c
20363         dbody.bgProperties = 'fixed'; // ie
20364         //Roo.DomHelper.applyStyles(dbody, ss);
20365         Roo.EventManager.on(this.doc, {
20366             //'mousedown': this.onEditorEvent,
20367             'mouseup': this.onEditorEvent,
20368             'dblclick': this.onEditorEvent,
20369             'click': this.onEditorEvent,
20370             'keyup': this.onEditorEvent,
20371             buffer:100,
20372             scope: this
20373         });
20374         if(Roo.isGecko){
20375             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20376         }
20377         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20378             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20379         }
20380         this.initialized = true;
20381
20382         this.owner.fireEvent('initialize', this);
20383         this.pushValue();
20384     },
20385
20386     // private
20387     onDestroy : function(){
20388         
20389         
20390         
20391         if(this.rendered){
20392             
20393             //for (var i =0; i < this.toolbars.length;i++) {
20394             //    // fixme - ask toolbars for heights?
20395             //    this.toolbars[i].onDestroy();
20396            // }
20397             
20398             //this.wrap.dom.innerHTML = '';
20399             //this.wrap.remove();
20400         }
20401     },
20402
20403     // private
20404     onFirstFocus : function(){
20405         
20406         this.assignDocWin();
20407         
20408         
20409         this.activated = true;
20410          
20411     
20412         if(Roo.isGecko){ // prevent silly gecko errors
20413             this.win.focus();
20414             var s = this.win.getSelection();
20415             if(!s.focusNode || s.focusNode.nodeType != 3){
20416                 var r = s.getRangeAt(0);
20417                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20418                 r.collapse(true);
20419                 this.deferFocus();
20420             }
20421             try{
20422                 this.execCmd('useCSS', true);
20423                 this.execCmd('styleWithCSS', false);
20424             }catch(e){}
20425         }
20426         this.owner.fireEvent('activate', this);
20427     },
20428
20429     // private
20430     adjustFont: function(btn){
20431         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20432         //if(Roo.isSafari){ // safari
20433         //    adjust *= 2;
20434        // }
20435         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20436         if(Roo.isSafari){ // safari
20437             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20438             v =  (v < 10) ? 10 : v;
20439             v =  (v > 48) ? 48 : v;
20440             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20441             
20442         }
20443         
20444         
20445         v = Math.max(1, v+adjust);
20446         
20447         this.execCmd('FontSize', v  );
20448     },
20449
20450     onEditorEvent : function(e)
20451     {
20452         this.owner.fireEvent('editorevent', this, e);
20453       //  this.updateToolbar();
20454         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20455     },
20456
20457     insertTag : function(tg)
20458     {
20459         // could be a bit smarter... -> wrap the current selected tRoo..
20460         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20461             
20462             range = this.createRange(this.getSelection());
20463             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20464             wrappingNode.appendChild(range.extractContents());
20465             range.insertNode(wrappingNode);
20466
20467             return;
20468             
20469             
20470             
20471         }
20472         this.execCmd("formatblock",   tg);
20473         
20474     },
20475     
20476     insertText : function(txt)
20477     {
20478         
20479         
20480         var range = this.createRange();
20481         range.deleteContents();
20482                //alert(Sender.getAttribute('label'));
20483                
20484         range.insertNode(this.doc.createTextNode(txt));
20485     } ,
20486     
20487      
20488
20489     /**
20490      * Executes a Midas editor command on the editor document and performs necessary focus and
20491      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20492      * @param {String} cmd The Midas command
20493      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20494      */
20495     relayCmd : function(cmd, value){
20496         this.win.focus();
20497         this.execCmd(cmd, value);
20498         this.owner.fireEvent('editorevent', this);
20499         //this.updateToolbar();
20500         this.owner.deferFocus();
20501     },
20502
20503     /**
20504      * Executes a Midas editor command directly on the editor document.
20505      * For visual commands, you should use {@link #relayCmd} instead.
20506      * <b>This should only be called after the editor is initialized.</b>
20507      * @param {String} cmd The Midas command
20508      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20509      */
20510     execCmd : function(cmd, value){
20511         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20512         this.syncValue();
20513     },
20514  
20515  
20516    
20517     /**
20518      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20519      * to insert tRoo.
20520      * @param {String} text | dom node.. 
20521      */
20522     insertAtCursor : function(text)
20523     {
20524         
20525         
20526         
20527         if(!this.activated){
20528             return;
20529         }
20530         /*
20531         if(Roo.isIE){
20532             this.win.focus();
20533             var r = this.doc.selection.createRange();
20534             if(r){
20535                 r.collapse(true);
20536                 r.pasteHTML(text);
20537                 this.syncValue();
20538                 this.deferFocus();
20539             
20540             }
20541             return;
20542         }
20543         */
20544         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20545             this.win.focus();
20546             
20547             
20548             // from jquery ui (MIT licenced)
20549             var range, node;
20550             var win = this.win;
20551             
20552             if (win.getSelection && win.getSelection().getRangeAt) {
20553                 range = win.getSelection().getRangeAt(0);
20554                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20555                 range.insertNode(node);
20556             } else if (win.document.selection && win.document.selection.createRange) {
20557                 // no firefox support
20558                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20559                 win.document.selection.createRange().pasteHTML(txt);
20560             } else {
20561                 // no firefox support
20562                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20563                 this.execCmd('InsertHTML', txt);
20564             } 
20565             
20566             this.syncValue();
20567             
20568             this.deferFocus();
20569         }
20570     },
20571  // private
20572     mozKeyPress : function(e){
20573         if(e.ctrlKey){
20574             var c = e.getCharCode(), cmd;
20575           
20576             if(c > 0){
20577                 c = String.fromCharCode(c).toLowerCase();
20578                 switch(c){
20579                     case 'b':
20580                         cmd = 'bold';
20581                         break;
20582                     case 'i':
20583                         cmd = 'italic';
20584                         break;
20585                     
20586                     case 'u':
20587                         cmd = 'underline';
20588                         break;
20589                     
20590                     case 'v':
20591                         this.cleanUpPaste.defer(100, this);
20592                         return;
20593                         
20594                 }
20595                 if(cmd){
20596                     this.win.focus();
20597                     this.execCmd(cmd);
20598                     this.deferFocus();
20599                     e.preventDefault();
20600                 }
20601                 
20602             }
20603         }
20604     },
20605
20606     // private
20607     fixKeys : function(){ // load time branching for fastest keydown performance
20608         if(Roo.isIE){
20609             return function(e){
20610                 var k = e.getKey(), r;
20611                 if(k == e.TAB){
20612                     e.stopEvent();
20613                     r = this.doc.selection.createRange();
20614                     if(r){
20615                         r.collapse(true);
20616                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20617                         this.deferFocus();
20618                     }
20619                     return;
20620                 }
20621                 
20622                 if(k == e.ENTER){
20623                     r = this.doc.selection.createRange();
20624                     if(r){
20625                         var target = r.parentElement();
20626                         if(!target || target.tagName.toLowerCase() != 'li'){
20627                             e.stopEvent();
20628                             r.pasteHTML('<br />');
20629                             r.collapse(false);
20630                             r.select();
20631                         }
20632                     }
20633                 }
20634                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20635                     this.cleanUpPaste.defer(100, this);
20636                     return;
20637                 }
20638                 
20639                 
20640             };
20641         }else if(Roo.isOpera){
20642             return function(e){
20643                 var k = e.getKey();
20644                 if(k == e.TAB){
20645                     e.stopEvent();
20646                     this.win.focus();
20647                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20648                     this.deferFocus();
20649                 }
20650                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20651                     this.cleanUpPaste.defer(100, this);
20652                     return;
20653                 }
20654                 
20655             };
20656         }else if(Roo.isSafari){
20657             return function(e){
20658                 var k = e.getKey();
20659                 
20660                 if(k == e.TAB){
20661                     e.stopEvent();
20662                     this.execCmd('InsertText','\t');
20663                     this.deferFocus();
20664                     return;
20665                 }
20666                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20667                     this.cleanUpPaste.defer(100, this);
20668                     return;
20669                 }
20670                 
20671              };
20672         }
20673     }(),
20674     
20675     getAllAncestors: function()
20676     {
20677         var p = this.getSelectedNode();
20678         var a = [];
20679         if (!p) {
20680             a.push(p); // push blank onto stack..
20681             p = this.getParentElement();
20682         }
20683         
20684         
20685         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20686             a.push(p);
20687             p = p.parentNode;
20688         }
20689         a.push(this.doc.body);
20690         return a;
20691     },
20692     lastSel : false,
20693     lastSelNode : false,
20694     
20695     
20696     getSelection : function() 
20697     {
20698         this.assignDocWin();
20699         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20700     },
20701     
20702     getSelectedNode: function() 
20703     {
20704         // this may only work on Gecko!!!
20705         
20706         // should we cache this!!!!
20707         
20708         
20709         
20710          
20711         var range = this.createRange(this.getSelection()).cloneRange();
20712         
20713         if (Roo.isIE) {
20714             var parent = range.parentElement();
20715             while (true) {
20716                 var testRange = range.duplicate();
20717                 testRange.moveToElementText(parent);
20718                 if (testRange.inRange(range)) {
20719                     break;
20720                 }
20721                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20722                     break;
20723                 }
20724                 parent = parent.parentElement;
20725             }
20726             return parent;
20727         }
20728         
20729         // is ancestor a text element.
20730         var ac =  range.commonAncestorContainer;
20731         if (ac.nodeType == 3) {
20732             ac = ac.parentNode;
20733         }
20734         
20735         var ar = ac.childNodes;
20736          
20737         var nodes = [];
20738         var other_nodes = [];
20739         var has_other_nodes = false;
20740         for (var i=0;i<ar.length;i++) {
20741             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20742                 continue;
20743             }
20744             // fullly contained node.
20745             
20746             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20747                 nodes.push(ar[i]);
20748                 continue;
20749             }
20750             
20751             // probably selected..
20752             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20753                 other_nodes.push(ar[i]);
20754                 continue;
20755             }
20756             // outer..
20757             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20758                 continue;
20759             }
20760             
20761             
20762             has_other_nodes = true;
20763         }
20764         if (!nodes.length && other_nodes.length) {
20765             nodes= other_nodes;
20766         }
20767         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20768             return false;
20769         }
20770         
20771         return nodes[0];
20772     },
20773     createRange: function(sel)
20774     {
20775         // this has strange effects when using with 
20776         // top toolbar - not sure if it's a great idea.
20777         //this.editor.contentWindow.focus();
20778         if (typeof sel != "undefined") {
20779             try {
20780                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20781             } catch(e) {
20782                 return this.doc.createRange();
20783             }
20784         } else {
20785             return this.doc.createRange();
20786         }
20787     },
20788     getParentElement: function()
20789     {
20790         
20791         this.assignDocWin();
20792         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20793         
20794         var range = this.createRange(sel);
20795          
20796         try {
20797             var p = range.commonAncestorContainer;
20798             while (p.nodeType == 3) { // text node
20799                 p = p.parentNode;
20800             }
20801             return p;
20802         } catch (e) {
20803             return null;
20804         }
20805     
20806     },
20807     /***
20808      *
20809      * Range intersection.. the hard stuff...
20810      *  '-1' = before
20811      *  '0' = hits..
20812      *  '1' = after.
20813      *         [ -- selected range --- ]
20814      *   [fail]                        [fail]
20815      *
20816      *    basically..
20817      *      if end is before start or  hits it. fail.
20818      *      if start is after end or hits it fail.
20819      *
20820      *   if either hits (but other is outside. - then it's not 
20821      *   
20822      *    
20823      **/
20824     
20825     
20826     // @see http://www.thismuchiknow.co.uk/?p=64.
20827     rangeIntersectsNode : function(range, node)
20828     {
20829         var nodeRange = node.ownerDocument.createRange();
20830         try {
20831             nodeRange.selectNode(node);
20832         } catch (e) {
20833             nodeRange.selectNodeContents(node);
20834         }
20835     
20836         var rangeStartRange = range.cloneRange();
20837         rangeStartRange.collapse(true);
20838     
20839         var rangeEndRange = range.cloneRange();
20840         rangeEndRange.collapse(false);
20841     
20842         var nodeStartRange = nodeRange.cloneRange();
20843         nodeStartRange.collapse(true);
20844     
20845         var nodeEndRange = nodeRange.cloneRange();
20846         nodeEndRange.collapse(false);
20847     
20848         return rangeStartRange.compareBoundaryPoints(
20849                  Range.START_TO_START, nodeEndRange) == -1 &&
20850                rangeEndRange.compareBoundaryPoints(
20851                  Range.START_TO_START, nodeStartRange) == 1;
20852         
20853          
20854     },
20855     rangeCompareNode : function(range, node)
20856     {
20857         var nodeRange = node.ownerDocument.createRange();
20858         try {
20859             nodeRange.selectNode(node);
20860         } catch (e) {
20861             nodeRange.selectNodeContents(node);
20862         }
20863         
20864         
20865         range.collapse(true);
20866     
20867         nodeRange.collapse(true);
20868      
20869         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20870         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20871          
20872         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20873         
20874         var nodeIsBefore   =  ss == 1;
20875         var nodeIsAfter    = ee == -1;
20876         
20877         if (nodeIsBefore && nodeIsAfter) {
20878             return 0; // outer
20879         }
20880         if (!nodeIsBefore && nodeIsAfter) {
20881             return 1; //right trailed.
20882         }
20883         
20884         if (nodeIsBefore && !nodeIsAfter) {
20885             return 2;  // left trailed.
20886         }
20887         // fully contined.
20888         return 3;
20889     },
20890
20891     // private? - in a new class?
20892     cleanUpPaste :  function()
20893     {
20894         // cleans up the whole document..
20895         Roo.log('cleanuppaste');
20896         
20897         this.cleanUpChildren(this.doc.body);
20898         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20899         if (clean != this.doc.body.innerHTML) {
20900             this.doc.body.innerHTML = clean;
20901         }
20902         
20903     },
20904     
20905     cleanWordChars : function(input) {// change the chars to hex code
20906         var he = Roo.HtmlEditorCore;
20907         
20908         var output = input;
20909         Roo.each(he.swapCodes, function(sw) { 
20910             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20911             
20912             output = output.replace(swapper, sw[1]);
20913         });
20914         
20915         return output;
20916     },
20917     
20918     
20919     cleanUpChildren : function (n)
20920     {
20921         if (!n.childNodes.length) {
20922             return;
20923         }
20924         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20925            this.cleanUpChild(n.childNodes[i]);
20926         }
20927     },
20928     
20929     
20930         
20931     
20932     cleanUpChild : function (node)
20933     {
20934         var ed = this;
20935         //console.log(node);
20936         if (node.nodeName == "#text") {
20937             // clean up silly Windows -- stuff?
20938             return; 
20939         }
20940         if (node.nodeName == "#comment") {
20941             node.parentNode.removeChild(node);
20942             // clean up silly Windows -- stuff?
20943             return; 
20944         }
20945         var lcname = node.tagName.toLowerCase();
20946         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20947         // whitelist of tags..
20948         
20949         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20950             // remove node.
20951             node.parentNode.removeChild(node);
20952             return;
20953             
20954         }
20955         
20956         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20957         
20958         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20959         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20960         
20961         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20962         //    remove_keep_children = true;
20963         //}
20964         
20965         if (remove_keep_children) {
20966             this.cleanUpChildren(node);
20967             // inserts everything just before this node...
20968             while (node.childNodes.length) {
20969                 var cn = node.childNodes[0];
20970                 node.removeChild(cn);
20971                 node.parentNode.insertBefore(cn, node);
20972             }
20973             node.parentNode.removeChild(node);
20974             return;
20975         }
20976         
20977         if (!node.attributes || !node.attributes.length) {
20978             this.cleanUpChildren(node);
20979             return;
20980         }
20981         
20982         function cleanAttr(n,v)
20983         {
20984             
20985             if (v.match(/^\./) || v.match(/^\//)) {
20986                 return;
20987             }
20988             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20989                 return;
20990             }
20991             if (v.match(/^#/)) {
20992                 return;
20993             }
20994 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20995             node.removeAttribute(n);
20996             
20997         }
20998         
20999         var cwhite = this.cwhite;
21000         var cblack = this.cblack;
21001             
21002         function cleanStyle(n,v)
21003         {
21004             if (v.match(/expression/)) { //XSS?? should we even bother..
21005                 node.removeAttribute(n);
21006                 return;
21007             }
21008             
21009             var parts = v.split(/;/);
21010             var clean = [];
21011             
21012             Roo.each(parts, function(p) {
21013                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21014                 if (!p.length) {
21015                     return true;
21016                 }
21017                 var l = p.split(':').shift().replace(/\s+/g,'');
21018                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21019                 
21020                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21021 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21022                     //node.removeAttribute(n);
21023                     return true;
21024                 }
21025                 //Roo.log()
21026                 // only allow 'c whitelisted system attributes'
21027                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21028 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21029                     //node.removeAttribute(n);
21030                     return true;
21031                 }
21032                 
21033                 
21034                  
21035                 
21036                 clean.push(p);
21037                 return true;
21038             });
21039             if (clean.length) { 
21040                 node.setAttribute(n, clean.join(';'));
21041             } else {
21042                 node.removeAttribute(n);
21043             }
21044             
21045         }
21046         
21047         
21048         for (var i = node.attributes.length-1; i > -1 ; i--) {
21049             var a = node.attributes[i];
21050             //console.log(a);
21051             
21052             if (a.name.toLowerCase().substr(0,2)=='on')  {
21053                 node.removeAttribute(a.name);
21054                 continue;
21055             }
21056             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21057                 node.removeAttribute(a.name);
21058                 continue;
21059             }
21060             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21061                 cleanAttr(a.name,a.value); // fixme..
21062                 continue;
21063             }
21064             if (a.name == 'style') {
21065                 cleanStyle(a.name,a.value);
21066                 continue;
21067             }
21068             /// clean up MS crap..
21069             // tecnically this should be a list of valid class'es..
21070             
21071             
21072             if (a.name == 'class') {
21073                 if (a.value.match(/^Mso/)) {
21074                     node.className = '';
21075                 }
21076                 
21077                 if (a.value.match(/body/)) {
21078                     node.className = '';
21079                 }
21080                 continue;
21081             }
21082             
21083             // style cleanup!?
21084             // class cleanup?
21085             
21086         }
21087         
21088         
21089         this.cleanUpChildren(node);
21090         
21091         
21092     },
21093     
21094     /**
21095      * Clean up MS wordisms...
21096      */
21097     cleanWord : function(node)
21098     {
21099         
21100         
21101         if (!node) {
21102             this.cleanWord(this.doc.body);
21103             return;
21104         }
21105         if (node.nodeName == "#text") {
21106             // clean up silly Windows -- stuff?
21107             return; 
21108         }
21109         if (node.nodeName == "#comment") {
21110             node.parentNode.removeChild(node);
21111             // clean up silly Windows -- stuff?
21112             return; 
21113         }
21114         
21115         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21116             node.parentNode.removeChild(node);
21117             return;
21118         }
21119         
21120         // remove - but keep children..
21121         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21122             while (node.childNodes.length) {
21123                 var cn = node.childNodes[0];
21124                 node.removeChild(cn);
21125                 node.parentNode.insertBefore(cn, node);
21126             }
21127             node.parentNode.removeChild(node);
21128             this.iterateChildren(node, this.cleanWord);
21129             return;
21130         }
21131         // clean styles
21132         if (node.className.length) {
21133             
21134             var cn = node.className.split(/\W+/);
21135             var cna = [];
21136             Roo.each(cn, function(cls) {
21137                 if (cls.match(/Mso[a-zA-Z]+/)) {
21138                     return;
21139                 }
21140                 cna.push(cls);
21141             });
21142             node.className = cna.length ? cna.join(' ') : '';
21143             if (!cna.length) {
21144                 node.removeAttribute("class");
21145             }
21146         }
21147         
21148         if (node.hasAttribute("lang")) {
21149             node.removeAttribute("lang");
21150         }
21151         
21152         if (node.hasAttribute("style")) {
21153             
21154             var styles = node.getAttribute("style").split(";");
21155             var nstyle = [];
21156             Roo.each(styles, function(s) {
21157                 if (!s.match(/:/)) {
21158                     return;
21159                 }
21160                 var kv = s.split(":");
21161                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21162                     return;
21163                 }
21164                 // what ever is left... we allow.
21165                 nstyle.push(s);
21166             });
21167             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21168             if (!nstyle.length) {
21169                 node.removeAttribute('style');
21170             }
21171         }
21172         this.iterateChildren(node, this.cleanWord);
21173         
21174         
21175         
21176     },
21177     /**
21178      * iterateChildren of a Node, calling fn each time, using this as the scole..
21179      * @param {DomNode} node node to iterate children of.
21180      * @param {Function} fn method of this class to call on each item.
21181      */
21182     iterateChildren : function(node, fn)
21183     {
21184         if (!node.childNodes.length) {
21185                 return;
21186         }
21187         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21188            fn.call(this, node.childNodes[i])
21189         }
21190     },
21191     
21192     
21193     /**
21194      * cleanTableWidths.
21195      *
21196      * Quite often pasting from word etc.. results in tables with column and widths.
21197      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21198      *
21199      */
21200     cleanTableWidths : function(node)
21201     {
21202          
21203          
21204         if (!node) {
21205             this.cleanTableWidths(this.doc.body);
21206             return;
21207         }
21208         
21209         // ignore list...
21210         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21211             return; 
21212         }
21213         Roo.log(node.tagName);
21214         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21215             this.iterateChildren(node, this.cleanTableWidths);
21216             return;
21217         }
21218         if (node.hasAttribute('width')) {
21219             node.removeAttribute('width');
21220         }
21221         
21222          
21223         if (node.hasAttribute("style")) {
21224             // pretty basic...
21225             
21226             var styles = node.getAttribute("style").split(";");
21227             var nstyle = [];
21228             Roo.each(styles, function(s) {
21229                 if (!s.match(/:/)) {
21230                     return;
21231                 }
21232                 var kv = s.split(":");
21233                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21234                     return;
21235                 }
21236                 // what ever is left... we allow.
21237                 nstyle.push(s);
21238             });
21239             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21240             if (!nstyle.length) {
21241                 node.removeAttribute('style');
21242             }
21243         }
21244         
21245         this.iterateChildren(node, this.cleanTableWidths);
21246         
21247         
21248     },
21249     
21250     
21251     
21252     
21253     domToHTML : function(currentElement, depth, nopadtext) {
21254         
21255         depth = depth || 0;
21256         nopadtext = nopadtext || false;
21257     
21258         if (!currentElement) {
21259             return this.domToHTML(this.doc.body);
21260         }
21261         
21262         //Roo.log(currentElement);
21263         var j;
21264         var allText = false;
21265         var nodeName = currentElement.nodeName;
21266         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21267         
21268         if  (nodeName == '#text') {
21269             
21270             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21271         }
21272         
21273         
21274         var ret = '';
21275         if (nodeName != 'BODY') {
21276              
21277             var i = 0;
21278             // Prints the node tagName, such as <A>, <IMG>, etc
21279             if (tagName) {
21280                 var attr = [];
21281                 for(i = 0; i < currentElement.attributes.length;i++) {
21282                     // quoting?
21283                     var aname = currentElement.attributes.item(i).name;
21284                     if (!currentElement.attributes.item(i).value.length) {
21285                         continue;
21286                     }
21287                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21288                 }
21289                 
21290                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21291             } 
21292             else {
21293                 
21294                 // eack
21295             }
21296         } else {
21297             tagName = false;
21298         }
21299         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21300             return ret;
21301         }
21302         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21303             nopadtext = true;
21304         }
21305         
21306         
21307         // Traverse the tree
21308         i = 0;
21309         var currentElementChild = currentElement.childNodes.item(i);
21310         var allText = true;
21311         var innerHTML  = '';
21312         lastnode = '';
21313         while (currentElementChild) {
21314             // Formatting code (indent the tree so it looks nice on the screen)
21315             var nopad = nopadtext;
21316             if (lastnode == 'SPAN') {
21317                 nopad  = true;
21318             }
21319             // text
21320             if  (currentElementChild.nodeName == '#text') {
21321                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21322                 toadd = nopadtext ? toadd : toadd.trim();
21323                 if (!nopad && toadd.length > 80) {
21324                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21325                 }
21326                 innerHTML  += toadd;
21327                 
21328                 i++;
21329                 currentElementChild = currentElement.childNodes.item(i);
21330                 lastNode = '';
21331                 continue;
21332             }
21333             allText = false;
21334             
21335             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21336                 
21337             // Recursively traverse the tree structure of the child node
21338             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21339             lastnode = currentElementChild.nodeName;
21340             i++;
21341             currentElementChild=currentElement.childNodes.item(i);
21342         }
21343         
21344         ret += innerHTML;
21345         
21346         if (!allText) {
21347                 // The remaining code is mostly for formatting the tree
21348             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21349         }
21350         
21351         
21352         if (tagName) {
21353             ret+= "</"+tagName+">";
21354         }
21355         return ret;
21356         
21357     },
21358         
21359     applyBlacklists : function()
21360     {
21361         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21362         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21363         
21364         this.white = [];
21365         this.black = [];
21366         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21367             if (b.indexOf(tag) > -1) {
21368                 return;
21369             }
21370             this.white.push(tag);
21371             
21372         }, this);
21373         
21374         Roo.each(w, function(tag) {
21375             if (b.indexOf(tag) > -1) {
21376                 return;
21377             }
21378             if (this.white.indexOf(tag) > -1) {
21379                 return;
21380             }
21381             this.white.push(tag);
21382             
21383         }, this);
21384         
21385         
21386         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21387             if (w.indexOf(tag) > -1) {
21388                 return;
21389             }
21390             this.black.push(tag);
21391             
21392         }, this);
21393         
21394         Roo.each(b, function(tag) {
21395             if (w.indexOf(tag) > -1) {
21396                 return;
21397             }
21398             if (this.black.indexOf(tag) > -1) {
21399                 return;
21400             }
21401             this.black.push(tag);
21402             
21403         }, this);
21404         
21405         
21406         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21407         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21408         
21409         this.cwhite = [];
21410         this.cblack = [];
21411         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21412             if (b.indexOf(tag) > -1) {
21413                 return;
21414             }
21415             this.cwhite.push(tag);
21416             
21417         }, this);
21418         
21419         Roo.each(w, function(tag) {
21420             if (b.indexOf(tag) > -1) {
21421                 return;
21422             }
21423             if (this.cwhite.indexOf(tag) > -1) {
21424                 return;
21425             }
21426             this.cwhite.push(tag);
21427             
21428         }, this);
21429         
21430         
21431         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21432             if (w.indexOf(tag) > -1) {
21433                 return;
21434             }
21435             this.cblack.push(tag);
21436             
21437         }, this);
21438         
21439         Roo.each(b, function(tag) {
21440             if (w.indexOf(tag) > -1) {
21441                 return;
21442             }
21443             if (this.cblack.indexOf(tag) > -1) {
21444                 return;
21445             }
21446             this.cblack.push(tag);
21447             
21448         }, this);
21449     },
21450     
21451     setStylesheets : function(stylesheets)
21452     {
21453         if(typeof(stylesheets) == 'string'){
21454             Roo.get(this.iframe.contentDocument.head).createChild({
21455                 tag : 'link',
21456                 rel : 'stylesheet',
21457                 type : 'text/css',
21458                 href : stylesheets
21459             });
21460             
21461             return;
21462         }
21463         var _this = this;
21464      
21465         Roo.each(stylesheets, function(s) {
21466             if(!s.length){
21467                 return;
21468             }
21469             
21470             Roo.get(_this.iframe.contentDocument.head).createChild({
21471                 tag : 'link',
21472                 rel : 'stylesheet',
21473                 type : 'text/css',
21474                 href : s
21475             });
21476         });
21477
21478         
21479     },
21480     
21481     removeStylesheets : function()
21482     {
21483         var _this = this;
21484         
21485         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21486             s.remove();
21487         });
21488     }
21489     
21490     // hide stuff that is not compatible
21491     /**
21492      * @event blur
21493      * @hide
21494      */
21495     /**
21496      * @event change
21497      * @hide
21498      */
21499     /**
21500      * @event focus
21501      * @hide
21502      */
21503     /**
21504      * @event specialkey
21505      * @hide
21506      */
21507     /**
21508      * @cfg {String} fieldClass @hide
21509      */
21510     /**
21511      * @cfg {String} focusClass @hide
21512      */
21513     /**
21514      * @cfg {String} autoCreate @hide
21515      */
21516     /**
21517      * @cfg {String} inputType @hide
21518      */
21519     /**
21520      * @cfg {String} invalidClass @hide
21521      */
21522     /**
21523      * @cfg {String} invalidText @hide
21524      */
21525     /**
21526      * @cfg {String} msgFx @hide
21527      */
21528     /**
21529      * @cfg {String} validateOnBlur @hide
21530      */
21531 });
21532
21533 Roo.HtmlEditorCore.white = [
21534         'area', 'br', 'img', 'input', 'hr', 'wbr',
21535         
21536        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21537        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21538        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21539        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21540        'table',   'ul',         'xmp', 
21541        
21542        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21543       'thead',   'tr', 
21544      
21545       'dir', 'menu', 'ol', 'ul', 'dl',
21546        
21547       'embed',  'object'
21548 ];
21549
21550
21551 Roo.HtmlEditorCore.black = [
21552     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21553         'applet', // 
21554         'base',   'basefont', 'bgsound', 'blink',  'body', 
21555         'frame',  'frameset', 'head',    'html',   'ilayer', 
21556         'iframe', 'layer',  'link',     'meta',    'object',   
21557         'script', 'style' ,'title',  'xml' // clean later..
21558 ];
21559 Roo.HtmlEditorCore.clean = [
21560     'script', 'style', 'title', 'xml'
21561 ];
21562 Roo.HtmlEditorCore.remove = [
21563     'font'
21564 ];
21565 // attributes..
21566
21567 Roo.HtmlEditorCore.ablack = [
21568     'on'
21569 ];
21570     
21571 Roo.HtmlEditorCore.aclean = [ 
21572     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21573 ];
21574
21575 // protocols..
21576 Roo.HtmlEditorCore.pwhite= [
21577         'http',  'https',  'mailto'
21578 ];
21579
21580 // white listed style attributes.
21581 Roo.HtmlEditorCore.cwhite= [
21582       //  'text-align', /// default is to allow most things..
21583       
21584          
21585 //        'font-size'//??
21586 ];
21587
21588 // black listed style attributes.
21589 Roo.HtmlEditorCore.cblack= [
21590       //  'font-size' -- this can be set by the project 
21591 ];
21592
21593
21594 Roo.HtmlEditorCore.swapCodes   =[ 
21595     [    8211, "--" ], 
21596     [    8212, "--" ], 
21597     [    8216,  "'" ],  
21598     [    8217, "'" ],  
21599     [    8220, '"' ],  
21600     [    8221, '"' ],  
21601     [    8226, "*" ],  
21602     [    8230, "..." ]
21603 ]; 
21604
21605     /*
21606  * - LGPL
21607  *
21608  * HtmlEditor
21609  * 
21610  */
21611
21612 /**
21613  * @class Roo.bootstrap.HtmlEditor
21614  * @extends Roo.bootstrap.TextArea
21615  * Bootstrap HtmlEditor class
21616
21617  * @constructor
21618  * Create a new HtmlEditor
21619  * @param {Object} config The config object
21620  */
21621
21622 Roo.bootstrap.HtmlEditor = function(config){
21623     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21624     if (!this.toolbars) {
21625         this.toolbars = [];
21626     }
21627     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21628     this.addEvents({
21629             /**
21630              * @event initialize
21631              * Fires when the editor is fully initialized (including the iframe)
21632              * @param {HtmlEditor} this
21633              */
21634             initialize: true,
21635             /**
21636              * @event activate
21637              * Fires when the editor is first receives the focus. Any insertion must wait
21638              * until after this event.
21639              * @param {HtmlEditor} this
21640              */
21641             activate: true,
21642              /**
21643              * @event beforesync
21644              * Fires before the textarea is updated with content from the editor iframe. Return false
21645              * to cancel the sync.
21646              * @param {HtmlEditor} this
21647              * @param {String} html
21648              */
21649             beforesync: true,
21650              /**
21651              * @event beforepush
21652              * Fires before the iframe editor is updated with content from the textarea. Return false
21653              * to cancel the push.
21654              * @param {HtmlEditor} this
21655              * @param {String} html
21656              */
21657             beforepush: true,
21658              /**
21659              * @event sync
21660              * Fires when the textarea is updated with content from the editor iframe.
21661              * @param {HtmlEditor} this
21662              * @param {String} html
21663              */
21664             sync: true,
21665              /**
21666              * @event push
21667              * Fires when the iframe editor is updated with content from the textarea.
21668              * @param {HtmlEditor} this
21669              * @param {String} html
21670              */
21671             push: true,
21672              /**
21673              * @event editmodechange
21674              * Fires when the editor switches edit modes
21675              * @param {HtmlEditor} this
21676              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21677              */
21678             editmodechange: true,
21679             /**
21680              * @event editorevent
21681              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21682              * @param {HtmlEditor} this
21683              */
21684             editorevent: true,
21685             /**
21686              * @event firstfocus
21687              * Fires when on first focus - needed by toolbars..
21688              * @param {HtmlEditor} this
21689              */
21690             firstfocus: true,
21691             /**
21692              * @event autosave
21693              * Auto save the htmlEditor value as a file into Events
21694              * @param {HtmlEditor} this
21695              */
21696             autosave: true,
21697             /**
21698              * @event savedpreview
21699              * preview the saved version of htmlEditor
21700              * @param {HtmlEditor} this
21701              */
21702             savedpreview: true
21703         });
21704 };
21705
21706
21707 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21708     
21709     
21710       /**
21711      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21712      */
21713     toolbars : false,
21714    
21715      /**
21716      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21717      *                        Roo.resizable.
21718      */
21719     resizable : false,
21720      /**
21721      * @cfg {Number} height (in pixels)
21722      */   
21723     height: 300,
21724    /**
21725      * @cfg {Number} width (in pixels)
21726      */   
21727     width: false,
21728     
21729     /**
21730      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21731      * 
21732      */
21733     stylesheets: false,
21734     
21735     // id of frame..
21736     frameId: false,
21737     
21738     // private properties
21739     validationEvent : false,
21740     deferHeight: true,
21741     initialized : false,
21742     activated : false,
21743     
21744     onFocus : Roo.emptyFn,
21745     iframePad:3,
21746     hideMode:'offsets',
21747     
21748     
21749     tbContainer : false,
21750     
21751     toolbarContainer :function() {
21752         return this.wrap.select('.x-html-editor-tb',true).first();
21753     },
21754
21755     /**
21756      * Protected method that will not generally be called directly. It
21757      * is called when the editor creates its toolbar. Override this method if you need to
21758      * add custom toolbar buttons.
21759      * @param {HtmlEditor} editor
21760      */
21761     createToolbar : function(){
21762         
21763         Roo.log("create toolbars");
21764         
21765         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21766         this.toolbars[0].render(this.toolbarContainer());
21767         
21768         return;
21769         
21770 //        if (!editor.toolbars || !editor.toolbars.length) {
21771 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21772 //        }
21773 //        
21774 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21775 //            editor.toolbars[i] = Roo.factory(
21776 //                    typeof(editor.toolbars[i]) == 'string' ?
21777 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21778 //                Roo.bootstrap.HtmlEditor);
21779 //            editor.toolbars[i].init(editor);
21780 //        }
21781     },
21782
21783      
21784     // private
21785     onRender : function(ct, position)
21786     {
21787        // Roo.log("Call onRender: " + this.xtype);
21788         var _t = this;
21789         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21790       
21791         this.wrap = this.inputEl().wrap({
21792             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21793         });
21794         
21795         this.editorcore.onRender(ct, position);
21796          
21797         if (this.resizable) {
21798             this.resizeEl = new Roo.Resizable(this.wrap, {
21799                 pinned : true,
21800                 wrap: true,
21801                 dynamic : true,
21802                 minHeight : this.height,
21803                 height: this.height,
21804                 handles : this.resizable,
21805                 width: this.width,
21806                 listeners : {
21807                     resize : function(r, w, h) {
21808                         _t.onResize(w,h); // -something
21809                     }
21810                 }
21811             });
21812             
21813         }
21814         this.createToolbar(this);
21815        
21816         
21817         if(!this.width && this.resizable){
21818             this.setSize(this.wrap.getSize());
21819         }
21820         if (this.resizeEl) {
21821             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21822             // should trigger onReize..
21823         }
21824         
21825     },
21826
21827     // private
21828     onResize : function(w, h)
21829     {
21830         Roo.log('resize: ' +w + ',' + h );
21831         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21832         var ew = false;
21833         var eh = false;
21834         
21835         if(this.inputEl() ){
21836             if(typeof w == 'number'){
21837                 var aw = w - this.wrap.getFrameWidth('lr');
21838                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21839                 ew = aw;
21840             }
21841             if(typeof h == 'number'){
21842                  var tbh = -11;  // fixme it needs to tool bar size!
21843                 for (var i =0; i < this.toolbars.length;i++) {
21844                     // fixme - ask toolbars for heights?
21845                     tbh += this.toolbars[i].el.getHeight();
21846                     //if (this.toolbars[i].footer) {
21847                     //    tbh += this.toolbars[i].footer.el.getHeight();
21848                     //}
21849                 }
21850               
21851                 
21852                 
21853                 
21854                 
21855                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21856                 ah -= 5; // knock a few pixes off for look..
21857                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21858                 var eh = ah;
21859             }
21860         }
21861         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21862         this.editorcore.onResize(ew,eh);
21863         
21864     },
21865
21866     /**
21867      * Toggles the editor between standard and source edit mode.
21868      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21869      */
21870     toggleSourceEdit : function(sourceEditMode)
21871     {
21872         this.editorcore.toggleSourceEdit(sourceEditMode);
21873         
21874         if(this.editorcore.sourceEditMode){
21875             Roo.log('editor - showing textarea');
21876             
21877 //            Roo.log('in');
21878 //            Roo.log(this.syncValue());
21879             this.syncValue();
21880             this.inputEl().removeClass(['hide', 'x-hidden']);
21881             this.inputEl().dom.removeAttribute('tabIndex');
21882             this.inputEl().focus();
21883         }else{
21884             Roo.log('editor - hiding textarea');
21885 //            Roo.log('out')
21886 //            Roo.log(this.pushValue()); 
21887             this.pushValue();
21888             
21889             this.inputEl().addClass(['hide', 'x-hidden']);
21890             this.inputEl().dom.setAttribute('tabIndex', -1);
21891             //this.deferFocus();
21892         }
21893          
21894         if(this.resizable){
21895             this.setSize(this.wrap.getSize());
21896         }
21897         
21898         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21899     },
21900  
21901     // private (for BoxComponent)
21902     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21903
21904     // private (for BoxComponent)
21905     getResizeEl : function(){
21906         return this.wrap;
21907     },
21908
21909     // private (for BoxComponent)
21910     getPositionEl : function(){
21911         return this.wrap;
21912     },
21913
21914     // private
21915     initEvents : function(){
21916         this.originalValue = this.getValue();
21917     },
21918
21919 //    /**
21920 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21921 //     * @method
21922 //     */
21923 //    markInvalid : Roo.emptyFn,
21924 //    /**
21925 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21926 //     * @method
21927 //     */
21928 //    clearInvalid : Roo.emptyFn,
21929
21930     setValue : function(v){
21931         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21932         this.editorcore.pushValue();
21933     },
21934
21935      
21936     // private
21937     deferFocus : function(){
21938         this.focus.defer(10, this);
21939     },
21940
21941     // doc'ed in Field
21942     focus : function(){
21943         this.editorcore.focus();
21944         
21945     },
21946       
21947
21948     // private
21949     onDestroy : function(){
21950         
21951         
21952         
21953         if(this.rendered){
21954             
21955             for (var i =0; i < this.toolbars.length;i++) {
21956                 // fixme - ask toolbars for heights?
21957                 this.toolbars[i].onDestroy();
21958             }
21959             
21960             this.wrap.dom.innerHTML = '';
21961             this.wrap.remove();
21962         }
21963     },
21964
21965     // private
21966     onFirstFocus : function(){
21967         //Roo.log("onFirstFocus");
21968         this.editorcore.onFirstFocus();
21969          for (var i =0; i < this.toolbars.length;i++) {
21970             this.toolbars[i].onFirstFocus();
21971         }
21972         
21973     },
21974     
21975     // private
21976     syncValue : function()
21977     {   
21978         this.editorcore.syncValue();
21979     },
21980     
21981     pushValue : function()
21982     {   
21983         this.editorcore.pushValue();
21984     }
21985      
21986     
21987     // hide stuff that is not compatible
21988     /**
21989      * @event blur
21990      * @hide
21991      */
21992     /**
21993      * @event change
21994      * @hide
21995      */
21996     /**
21997      * @event focus
21998      * @hide
21999      */
22000     /**
22001      * @event specialkey
22002      * @hide
22003      */
22004     /**
22005      * @cfg {String} fieldClass @hide
22006      */
22007     /**
22008      * @cfg {String} focusClass @hide
22009      */
22010     /**
22011      * @cfg {String} autoCreate @hide
22012      */
22013     /**
22014      * @cfg {String} inputType @hide
22015      */
22016     /**
22017      * @cfg {String} invalidClass @hide
22018      */
22019     /**
22020      * @cfg {String} invalidText @hide
22021      */
22022     /**
22023      * @cfg {String} msgFx @hide
22024      */
22025     /**
22026      * @cfg {String} validateOnBlur @hide
22027      */
22028 });
22029  
22030     
22031    
22032    
22033    
22034       
22035 Roo.namespace('Roo.bootstrap.htmleditor');
22036 /**
22037  * @class Roo.bootstrap.HtmlEditorToolbar1
22038  * Basic Toolbar
22039  * 
22040  * Usage:
22041  *
22042  new Roo.bootstrap.HtmlEditor({
22043     ....
22044     toolbars : [
22045         new Roo.bootstrap.HtmlEditorToolbar1({
22046             disable : { fonts: 1 , format: 1, ..., ... , ...],
22047             btns : [ .... ]
22048         })
22049     }
22050      
22051  * 
22052  * @cfg {Object} disable List of elements to disable..
22053  * @cfg {Array} btns List of additional buttons.
22054  * 
22055  * 
22056  * NEEDS Extra CSS? 
22057  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22058  */
22059  
22060 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22061 {
22062     
22063     Roo.apply(this, config);
22064     
22065     // default disabled, based on 'good practice'..
22066     this.disable = this.disable || {};
22067     Roo.applyIf(this.disable, {
22068         fontSize : true,
22069         colors : true,
22070         specialElements : true
22071     });
22072     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22073     
22074     this.editor = config.editor;
22075     this.editorcore = config.editor.editorcore;
22076     
22077     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22078     
22079     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22080     // dont call parent... till later.
22081 }
22082 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22083      
22084     bar : true,
22085     
22086     editor : false,
22087     editorcore : false,
22088     
22089     
22090     formats : [
22091         "p" ,  
22092         "h1","h2","h3","h4","h5","h6", 
22093         "pre", "code", 
22094         "abbr", "acronym", "address", "cite", "samp", "var",
22095         'div','span'
22096     ],
22097     
22098     onRender : function(ct, position)
22099     {
22100        // Roo.log("Call onRender: " + this.xtype);
22101         
22102        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22103        Roo.log(this.el);
22104        this.el.dom.style.marginBottom = '0';
22105        var _this = this;
22106        var editorcore = this.editorcore;
22107        var editor= this.editor;
22108        
22109        var children = [];
22110        var btn = function(id,cmd , toggle, handler){
22111        
22112             var  event = toggle ? 'toggle' : 'click';
22113        
22114             var a = {
22115                 size : 'sm',
22116                 xtype: 'Button',
22117                 xns: Roo.bootstrap,
22118                 glyphicon : id,
22119                 cmd : id || cmd,
22120                 enableToggle:toggle !== false,
22121                 //html : 'submit'
22122                 pressed : toggle ? false : null,
22123                 listeners : {}
22124             };
22125             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22126                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22127             };
22128             children.push(a);
22129             return a;
22130        }
22131         
22132         var style = {
22133                 xtype: 'Button',
22134                 size : 'sm',
22135                 xns: Roo.bootstrap,
22136                 glyphicon : 'font',
22137                 //html : 'submit'
22138                 menu : {
22139                     xtype: 'Menu',
22140                     xns: Roo.bootstrap,
22141                     items:  []
22142                 }
22143         };
22144         Roo.each(this.formats, function(f) {
22145             style.menu.items.push({
22146                 xtype :'MenuItem',
22147                 xns: Roo.bootstrap,
22148                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22149                 tagname : f,
22150                 listeners : {
22151                     click : function()
22152                     {
22153                         editorcore.insertTag(this.tagname);
22154                         editor.focus();
22155                     }
22156                 }
22157                 
22158             });
22159         });
22160          children.push(style);   
22161             
22162             
22163         btn('bold',false,true);
22164         btn('italic',false,true);
22165         btn('align-left', 'justifyleft',true);
22166         btn('align-center', 'justifycenter',true);
22167         btn('align-right' , 'justifyright',true);
22168         btn('link', false, false, function(btn) {
22169             //Roo.log("create link?");
22170             var url = prompt(this.createLinkText, this.defaultLinkValue);
22171             if(url && url != 'http:/'+'/'){
22172                 this.editorcore.relayCmd('createlink', url);
22173             }
22174         }),
22175         btn('list','insertunorderedlist',true);
22176         btn('pencil', false,true, function(btn){
22177                 Roo.log(this);
22178                 
22179                 this.toggleSourceEdit(btn.pressed);
22180         });
22181         /*
22182         var cog = {
22183                 xtype: 'Button',
22184                 size : 'sm',
22185                 xns: Roo.bootstrap,
22186                 glyphicon : 'cog',
22187                 //html : 'submit'
22188                 menu : {
22189                     xtype: 'Menu',
22190                     xns: Roo.bootstrap,
22191                     items:  []
22192                 }
22193         };
22194         
22195         cog.menu.items.push({
22196             xtype :'MenuItem',
22197             xns: Roo.bootstrap,
22198             html : Clean styles,
22199             tagname : f,
22200             listeners : {
22201                 click : function()
22202                 {
22203                     editorcore.insertTag(this.tagname);
22204                     editor.focus();
22205                 }
22206             }
22207             
22208         });
22209        */
22210         
22211          
22212        this.xtype = 'NavSimplebar';
22213         
22214         for(var i=0;i< children.length;i++) {
22215             
22216             this.buttons.add(this.addxtypeChild(children[i]));
22217             
22218         }
22219         
22220         editor.on('editorevent', this.updateToolbar, this);
22221     },
22222     onBtnClick : function(id)
22223     {
22224        this.editorcore.relayCmd(id);
22225        this.editorcore.focus();
22226     },
22227     
22228     /**
22229      * Protected method that will not generally be called directly. It triggers
22230      * a toolbar update by reading the markup state of the current selection in the editor.
22231      */
22232     updateToolbar: function(){
22233
22234         if(!this.editorcore.activated){
22235             this.editor.onFirstFocus(); // is this neeed?
22236             return;
22237         }
22238
22239         var btns = this.buttons; 
22240         var doc = this.editorcore.doc;
22241         btns.get('bold').setActive(doc.queryCommandState('bold'));
22242         btns.get('italic').setActive(doc.queryCommandState('italic'));
22243         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22244         
22245         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22246         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22247         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22248         
22249         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22250         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22251          /*
22252         
22253         var ans = this.editorcore.getAllAncestors();
22254         if (this.formatCombo) {
22255             
22256             
22257             var store = this.formatCombo.store;
22258             this.formatCombo.setValue("");
22259             for (var i =0; i < ans.length;i++) {
22260                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22261                     // select it..
22262                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22263                     break;
22264                 }
22265             }
22266         }
22267         
22268         
22269         
22270         // hides menus... - so this cant be on a menu...
22271         Roo.bootstrap.MenuMgr.hideAll();
22272         */
22273         Roo.bootstrap.MenuMgr.hideAll();
22274         //this.editorsyncValue();
22275     },
22276     onFirstFocus: function() {
22277         this.buttons.each(function(item){
22278            item.enable();
22279         });
22280     },
22281     toggleSourceEdit : function(sourceEditMode){
22282         
22283           
22284         if(sourceEditMode){
22285             Roo.log("disabling buttons");
22286            this.buttons.each( function(item){
22287                 if(item.cmd != 'pencil'){
22288                     item.disable();
22289                 }
22290             });
22291           
22292         }else{
22293             Roo.log("enabling buttons");
22294             if(this.editorcore.initialized){
22295                 this.buttons.each( function(item){
22296                     item.enable();
22297                 });
22298             }
22299             
22300         }
22301         Roo.log("calling toggole on editor");
22302         // tell the editor that it's been pressed..
22303         this.editor.toggleSourceEdit(sourceEditMode);
22304        
22305     }
22306 });
22307
22308
22309
22310
22311
22312 /**
22313  * @class Roo.bootstrap.Table.AbstractSelectionModel
22314  * @extends Roo.util.Observable
22315  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22316  * implemented by descendant classes.  This class should not be directly instantiated.
22317  * @constructor
22318  */
22319 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22320     this.locked = false;
22321     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22322 };
22323
22324
22325 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22326     /** @ignore Called by the grid automatically. Do not call directly. */
22327     init : function(grid){
22328         this.grid = grid;
22329         this.initEvents();
22330     },
22331
22332     /**
22333      * Locks the selections.
22334      */
22335     lock : function(){
22336         this.locked = true;
22337     },
22338
22339     /**
22340      * Unlocks the selections.
22341      */
22342     unlock : function(){
22343         this.locked = false;
22344     },
22345
22346     /**
22347      * Returns true if the selections are locked.
22348      * @return {Boolean}
22349      */
22350     isLocked : function(){
22351         return this.locked;
22352     }
22353 });
22354 /**
22355  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22356  * @class Roo.bootstrap.Table.RowSelectionModel
22357  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22358  * It supports multiple selections and keyboard selection/navigation. 
22359  * @constructor
22360  * @param {Object} config
22361  */
22362
22363 Roo.bootstrap.Table.RowSelectionModel = function(config){
22364     Roo.apply(this, config);
22365     this.selections = new Roo.util.MixedCollection(false, function(o){
22366         return o.id;
22367     });
22368
22369     this.last = false;
22370     this.lastActive = false;
22371
22372     this.addEvents({
22373         /**
22374              * @event selectionchange
22375              * Fires when the selection changes
22376              * @param {SelectionModel} this
22377              */
22378             "selectionchange" : true,
22379         /**
22380              * @event afterselectionchange
22381              * Fires after the selection changes (eg. by key press or clicking)
22382              * @param {SelectionModel} this
22383              */
22384             "afterselectionchange" : true,
22385         /**
22386              * @event beforerowselect
22387              * Fires when a row is selected being selected, return false to cancel.
22388              * @param {SelectionModel} this
22389              * @param {Number} rowIndex The selected index
22390              * @param {Boolean} keepExisting False if other selections will be cleared
22391              */
22392             "beforerowselect" : true,
22393         /**
22394              * @event rowselect
22395              * Fires when a row is selected.
22396              * @param {SelectionModel} this
22397              * @param {Number} rowIndex The selected index
22398              * @param {Roo.data.Record} r The record
22399              */
22400             "rowselect" : true,
22401         /**
22402              * @event rowdeselect
22403              * Fires when a row is deselected.
22404              * @param {SelectionModel} this
22405              * @param {Number} rowIndex The selected index
22406              */
22407         "rowdeselect" : true
22408     });
22409     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22410     this.locked = false;
22411  };
22412
22413 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22414     /**
22415      * @cfg {Boolean} singleSelect
22416      * True to allow selection of only one row at a time (defaults to false)
22417      */
22418     singleSelect : false,
22419
22420     // private
22421     initEvents : function()
22422     {
22423
22424         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22425         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22426         //}else{ // allow click to work like normal
22427          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22428         //}
22429         this.grid.on("rowclick", this.handleMouseDown, this);
22430         
22431         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22432             "up" : function(e){
22433                 if(!e.shiftKey){
22434                     this.selectPrevious(e.shiftKey);
22435                 }else if(this.last !== false && this.lastActive !== false){
22436                     var last = this.last;
22437                     this.selectRange(this.last,  this.lastActive-1);
22438                     this.grid.getView().focusRow(this.lastActive);
22439                     if(last !== false){
22440                         this.last = last;
22441                     }
22442                 }else{
22443                     this.selectFirstRow();
22444                 }
22445                 this.fireEvent("afterselectionchange", this);
22446             },
22447             "down" : function(e){
22448                 if(!e.shiftKey){
22449                     this.selectNext(e.shiftKey);
22450                 }else if(this.last !== false && this.lastActive !== false){
22451                     var last = this.last;
22452                     this.selectRange(this.last,  this.lastActive+1);
22453                     this.grid.getView().focusRow(this.lastActive);
22454                     if(last !== false){
22455                         this.last = last;
22456                     }
22457                 }else{
22458                     this.selectFirstRow();
22459                 }
22460                 this.fireEvent("afterselectionchange", this);
22461             },
22462             scope: this
22463         });
22464         /*
22465         var view = this.grid.view;
22466         view.on("refresh", this.onRefresh, this);
22467         view.on("rowupdated", this.onRowUpdated, this);
22468         view.on("rowremoved", this.onRemove, this);
22469         */
22470     },
22471
22472     // private
22473     onRefresh : function(){
22474         var ds = this.grid.dataSource, i, v = this.grid.view;
22475         var s = this.selections;
22476         s.each(function(r){
22477             if((i = ds.indexOfId(r.id)) != -1){
22478                 v.onRowSelect(i);
22479             }else{
22480                 s.remove(r);
22481             }
22482         });
22483     },
22484
22485     // private
22486     onRemove : function(v, index, r){
22487         this.selections.remove(r);
22488     },
22489
22490     // private
22491     onRowUpdated : function(v, index, r){
22492         if(this.isSelected(r)){
22493             v.onRowSelect(index);
22494         }
22495     },
22496
22497     /**
22498      * Select records.
22499      * @param {Array} records The records to select
22500      * @param {Boolean} keepExisting (optional) True to keep existing selections
22501      */
22502     selectRecords : function(records, keepExisting){
22503         if(!keepExisting){
22504             this.clearSelections();
22505         }
22506         var ds = this.grid.dataSource;
22507         for(var i = 0, len = records.length; i < len; i++){
22508             this.selectRow(ds.indexOf(records[i]), true);
22509         }
22510     },
22511
22512     /**
22513      * Gets the number of selected rows.
22514      * @return {Number}
22515      */
22516     getCount : function(){
22517         return this.selections.length;
22518     },
22519
22520     /**
22521      * Selects the first row in the grid.
22522      */
22523     selectFirstRow : function(){
22524         this.selectRow(0);
22525     },
22526
22527     /**
22528      * Select the last row.
22529      * @param {Boolean} keepExisting (optional) True to keep existing selections
22530      */
22531     selectLastRow : function(keepExisting){
22532         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22533     },
22534
22535     /**
22536      * Selects the row immediately following the last selected row.
22537      * @param {Boolean} keepExisting (optional) True to keep existing selections
22538      */
22539     selectNext : function(keepExisting){
22540         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22541             this.selectRow(this.last+1, keepExisting);
22542             this.grid.getView().focusRow(this.last);
22543         }
22544     },
22545
22546     /**
22547      * Selects the row that precedes the last selected row.
22548      * @param {Boolean} keepExisting (optional) True to keep existing selections
22549      */
22550     selectPrevious : function(keepExisting){
22551         if(this.last){
22552             this.selectRow(this.last-1, keepExisting);
22553             this.grid.getView().focusRow(this.last);
22554         }
22555     },
22556
22557     /**
22558      * Returns the selected records
22559      * @return {Array} Array of selected records
22560      */
22561     getSelections : function(){
22562         return [].concat(this.selections.items);
22563     },
22564
22565     /**
22566      * Returns the first selected record.
22567      * @return {Record}
22568      */
22569     getSelected : function(){
22570         return this.selections.itemAt(0);
22571     },
22572
22573
22574     /**
22575      * Clears all selections.
22576      */
22577     clearSelections : function(fast){
22578         if(this.locked) {
22579             return;
22580         }
22581         if(fast !== true){
22582             var ds = this.grid.dataSource;
22583             var s = this.selections;
22584             s.each(function(r){
22585                 this.deselectRow(ds.indexOfId(r.id));
22586             }, this);
22587             s.clear();
22588         }else{
22589             this.selections.clear();
22590         }
22591         this.last = false;
22592     },
22593
22594
22595     /**
22596      * Selects all rows.
22597      */
22598     selectAll : function(){
22599         if(this.locked) {
22600             return;
22601         }
22602         this.selections.clear();
22603         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22604             this.selectRow(i, true);
22605         }
22606     },
22607
22608     /**
22609      * Returns True if there is a selection.
22610      * @return {Boolean}
22611      */
22612     hasSelection : function(){
22613         return this.selections.length > 0;
22614     },
22615
22616     /**
22617      * Returns True if the specified row is selected.
22618      * @param {Number/Record} record The record or index of the record to check
22619      * @return {Boolean}
22620      */
22621     isSelected : function(index){
22622         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22623         return (r && this.selections.key(r.id) ? true : false);
22624     },
22625
22626     /**
22627      * Returns True if the specified record id is selected.
22628      * @param {String} id The id of record to check
22629      * @return {Boolean}
22630      */
22631     isIdSelected : function(id){
22632         return (this.selections.key(id) ? true : false);
22633     },
22634
22635     // private
22636     handleMouseDown : function(e, t){
22637         var view = this.grid.getView(), rowIndex;
22638         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22639             return;
22640         };
22641         if(e.shiftKey && this.last !== false){
22642             var last = this.last;
22643             this.selectRange(last, rowIndex, e.ctrlKey);
22644             this.last = last; // reset the last
22645             view.focusRow(rowIndex);
22646         }else{
22647             var isSelected = this.isSelected(rowIndex);
22648             if(e.button !== 0 && isSelected){
22649                 view.focusRow(rowIndex);
22650             }else if(e.ctrlKey && isSelected){
22651                 this.deselectRow(rowIndex);
22652             }else if(!isSelected){
22653                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22654                 view.focusRow(rowIndex);
22655             }
22656         }
22657         this.fireEvent("afterselectionchange", this);
22658     },
22659     // private
22660     handleDragableRowClick :  function(grid, rowIndex, e) 
22661     {
22662         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22663             this.selectRow(rowIndex, false);
22664             grid.view.focusRow(rowIndex);
22665              this.fireEvent("afterselectionchange", this);
22666         }
22667     },
22668     
22669     /**
22670      * Selects multiple rows.
22671      * @param {Array} rows Array of the indexes of the row to select
22672      * @param {Boolean} keepExisting (optional) True to keep existing selections
22673      */
22674     selectRows : function(rows, keepExisting){
22675         if(!keepExisting){
22676             this.clearSelections();
22677         }
22678         for(var i = 0, len = rows.length; i < len; i++){
22679             this.selectRow(rows[i], true);
22680         }
22681     },
22682
22683     /**
22684      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22685      * @param {Number} startRow The index of the first row in the range
22686      * @param {Number} endRow The index of the last row in the range
22687      * @param {Boolean} keepExisting (optional) True to retain existing selections
22688      */
22689     selectRange : function(startRow, endRow, keepExisting){
22690         if(this.locked) {
22691             return;
22692         }
22693         if(!keepExisting){
22694             this.clearSelections();
22695         }
22696         if(startRow <= endRow){
22697             for(var i = startRow; i <= endRow; i++){
22698                 this.selectRow(i, true);
22699             }
22700         }else{
22701             for(var i = startRow; i >= endRow; i--){
22702                 this.selectRow(i, true);
22703             }
22704         }
22705     },
22706
22707     /**
22708      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22709      * @param {Number} startRow The index of the first row in the range
22710      * @param {Number} endRow The index of the last row in the range
22711      */
22712     deselectRange : function(startRow, endRow, preventViewNotify){
22713         if(this.locked) {
22714             return;
22715         }
22716         for(var i = startRow; i <= endRow; i++){
22717             this.deselectRow(i, preventViewNotify);
22718         }
22719     },
22720
22721     /**
22722      * Selects a row.
22723      * @param {Number} row The index of the row to select
22724      * @param {Boolean} keepExisting (optional) True to keep existing selections
22725      */
22726     selectRow : function(index, keepExisting, preventViewNotify){
22727         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22728             return;
22729         }
22730         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22731             if(!keepExisting || this.singleSelect){
22732                 this.clearSelections();
22733             }
22734             var r = this.grid.dataSource.getAt(index);
22735             this.selections.add(r);
22736             this.last = this.lastActive = index;
22737             if(!preventViewNotify){
22738                 this.grid.getView().onRowSelect(index);
22739             }
22740             this.fireEvent("rowselect", this, index, r);
22741             this.fireEvent("selectionchange", this);
22742         }
22743     },
22744
22745     /**
22746      * Deselects a row.
22747      * @param {Number} row The index of the row to deselect
22748      */
22749     deselectRow : function(index, preventViewNotify){
22750         if(this.locked) {
22751             return;
22752         }
22753         if(this.last == index){
22754             this.last = false;
22755         }
22756         if(this.lastActive == index){
22757             this.lastActive = false;
22758         }
22759         var r = this.grid.dataSource.getAt(index);
22760         this.selections.remove(r);
22761         if(!preventViewNotify){
22762             this.grid.getView().onRowDeselect(index);
22763         }
22764         this.fireEvent("rowdeselect", this, index);
22765         this.fireEvent("selectionchange", this);
22766     },
22767
22768     // private
22769     restoreLast : function(){
22770         if(this._last){
22771             this.last = this._last;
22772         }
22773     },
22774
22775     // private
22776     acceptsNav : function(row, col, cm){
22777         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22778     },
22779
22780     // private
22781     onEditorKey : function(field, e){
22782         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22783         if(k == e.TAB){
22784             e.stopEvent();
22785             ed.completeEdit();
22786             if(e.shiftKey){
22787                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22788             }else{
22789                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22790             }
22791         }else if(k == e.ENTER && !e.ctrlKey){
22792             e.stopEvent();
22793             ed.completeEdit();
22794             if(e.shiftKey){
22795                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22796             }else{
22797                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22798             }
22799         }else if(k == e.ESC){
22800             ed.cancelEdit();
22801         }
22802         if(newCell){
22803             g.startEditing(newCell[0], newCell[1]);
22804         }
22805     }
22806 });/*
22807  * Based on:
22808  * Ext JS Library 1.1.1
22809  * Copyright(c) 2006-2007, Ext JS, LLC.
22810  *
22811  * Originally Released Under LGPL - original licence link has changed is not relivant.
22812  *
22813  * Fork - LGPL
22814  * <script type="text/javascript">
22815  */
22816  
22817 /**
22818  * @class Roo.bootstrap.PagingToolbar
22819  * @extends Roo.bootstrap.NavSimplebar
22820  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22821  * @constructor
22822  * Create a new PagingToolbar
22823  * @param {Object} config The config object
22824  * @param {Roo.data.Store} store
22825  */
22826 Roo.bootstrap.PagingToolbar = function(config)
22827 {
22828     // old args format still supported... - xtype is prefered..
22829         // created from xtype...
22830     
22831     this.ds = config.dataSource;
22832     
22833     if (config.store && !this.ds) {
22834         this.store= Roo.factory(config.store, Roo.data);
22835         this.ds = this.store;
22836         this.ds.xmodule = this.xmodule || false;
22837     }
22838     
22839     this.toolbarItems = [];
22840     if (config.items) {
22841         this.toolbarItems = config.items;
22842     }
22843     
22844     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22845     
22846     this.cursor = 0;
22847     
22848     if (this.ds) { 
22849         this.bind(this.ds);
22850     }
22851     
22852     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22853     
22854 };
22855
22856 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22857     /**
22858      * @cfg {Roo.data.Store} dataSource
22859      * The underlying data store providing the paged data
22860      */
22861     /**
22862      * @cfg {String/HTMLElement/Element} container
22863      * container The id or element that will contain the toolbar
22864      */
22865     /**
22866      * @cfg {Boolean} displayInfo
22867      * True to display the displayMsg (defaults to false)
22868      */
22869     /**
22870      * @cfg {Number} pageSize
22871      * The number of records to display per page (defaults to 20)
22872      */
22873     pageSize: 20,
22874     /**
22875      * @cfg {String} displayMsg
22876      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22877      */
22878     displayMsg : 'Displaying {0} - {1} of {2}',
22879     /**
22880      * @cfg {String} emptyMsg
22881      * The message to display when no records are found (defaults to "No data to display")
22882      */
22883     emptyMsg : 'No data to display',
22884     /**
22885      * Customizable piece of the default paging text (defaults to "Page")
22886      * @type String
22887      */
22888     beforePageText : "Page",
22889     /**
22890      * Customizable piece of the default paging text (defaults to "of %0")
22891      * @type String
22892      */
22893     afterPageText : "of {0}",
22894     /**
22895      * Customizable piece of the default paging text (defaults to "First Page")
22896      * @type String
22897      */
22898     firstText : "First Page",
22899     /**
22900      * Customizable piece of the default paging text (defaults to "Previous Page")
22901      * @type String
22902      */
22903     prevText : "Previous Page",
22904     /**
22905      * Customizable piece of the default paging text (defaults to "Next Page")
22906      * @type String
22907      */
22908     nextText : "Next Page",
22909     /**
22910      * Customizable piece of the default paging text (defaults to "Last Page")
22911      * @type String
22912      */
22913     lastText : "Last Page",
22914     /**
22915      * Customizable piece of the default paging text (defaults to "Refresh")
22916      * @type String
22917      */
22918     refreshText : "Refresh",
22919
22920     buttons : false,
22921     // private
22922     onRender : function(ct, position) 
22923     {
22924         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22925         this.navgroup.parentId = this.id;
22926         this.navgroup.onRender(this.el, null);
22927         // add the buttons to the navgroup
22928         
22929         if(this.displayInfo){
22930             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22931             this.displayEl = this.el.select('.x-paging-info', true).first();
22932 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22933 //            this.displayEl = navel.el.select('span',true).first();
22934         }
22935         
22936         var _this = this;
22937         
22938         if(this.buttons){
22939             Roo.each(_this.buttons, function(e){ // this might need to use render????
22940                Roo.factory(e).onRender(_this.el, null);
22941             });
22942         }
22943             
22944         Roo.each(_this.toolbarItems, function(e) {
22945             _this.navgroup.addItem(e);
22946         });
22947         
22948         
22949         this.first = this.navgroup.addItem({
22950             tooltip: this.firstText,
22951             cls: "prev",
22952             icon : 'fa fa-backward',
22953             disabled: true,
22954             preventDefault: true,
22955             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22956         });
22957         
22958         this.prev =  this.navgroup.addItem({
22959             tooltip: this.prevText,
22960             cls: "prev",
22961             icon : 'fa fa-step-backward',
22962             disabled: true,
22963             preventDefault: true,
22964             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22965         });
22966     //this.addSeparator();
22967         
22968         
22969         var field = this.navgroup.addItem( {
22970             tagtype : 'span',
22971             cls : 'x-paging-position',
22972             
22973             html : this.beforePageText  +
22974                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22975                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22976          } ); //?? escaped?
22977         
22978         this.field = field.el.select('input', true).first();
22979         this.field.on("keydown", this.onPagingKeydown, this);
22980         this.field.on("focus", function(){this.dom.select();});
22981     
22982     
22983         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22984         //this.field.setHeight(18);
22985         //this.addSeparator();
22986         this.next = this.navgroup.addItem({
22987             tooltip: this.nextText,
22988             cls: "next",
22989             html : ' <i class="fa fa-step-forward">',
22990             disabled: true,
22991             preventDefault: true,
22992             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22993         });
22994         this.last = this.navgroup.addItem({
22995             tooltip: this.lastText,
22996             icon : 'fa fa-forward',
22997             cls: "next",
22998             disabled: true,
22999             preventDefault: true,
23000             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23001         });
23002     //this.addSeparator();
23003         this.loading = this.navgroup.addItem({
23004             tooltip: this.refreshText,
23005             icon: 'fa fa-refresh',
23006             preventDefault: true,
23007             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23008         });
23009         
23010     },
23011
23012     // private
23013     updateInfo : function(){
23014         if(this.displayEl){
23015             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23016             var msg = count == 0 ?
23017                 this.emptyMsg :
23018                 String.format(
23019                     this.displayMsg,
23020                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23021                 );
23022             this.displayEl.update(msg);
23023         }
23024     },
23025
23026     // private
23027     onLoad : function(ds, r, o){
23028        this.cursor = o.params ? o.params.start : 0;
23029        var d = this.getPageData(),
23030             ap = d.activePage,
23031             ps = d.pages;
23032         
23033        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23034        this.field.dom.value = ap;
23035        this.first.setDisabled(ap == 1);
23036        this.prev.setDisabled(ap == 1);
23037        this.next.setDisabled(ap == ps);
23038        this.last.setDisabled(ap == ps);
23039        this.loading.enable();
23040        this.updateInfo();
23041     },
23042
23043     // private
23044     getPageData : function(){
23045         var total = this.ds.getTotalCount();
23046         return {
23047             total : total,
23048             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23049             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23050         };
23051     },
23052
23053     // private
23054     onLoadError : function(){
23055         this.loading.enable();
23056     },
23057
23058     // private
23059     onPagingKeydown : function(e){
23060         var k = e.getKey();
23061         var d = this.getPageData();
23062         if(k == e.RETURN){
23063             var v = this.field.dom.value, pageNum;
23064             if(!v || isNaN(pageNum = parseInt(v, 10))){
23065                 this.field.dom.value = d.activePage;
23066                 return;
23067             }
23068             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23069             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23070             e.stopEvent();
23071         }
23072         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))
23073         {
23074           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23075           this.field.dom.value = pageNum;
23076           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23077           e.stopEvent();
23078         }
23079         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23080         {
23081           var v = this.field.dom.value, pageNum; 
23082           var increment = (e.shiftKey) ? 10 : 1;
23083           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23084                 increment *= -1;
23085           }
23086           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23087             this.field.dom.value = d.activePage;
23088             return;
23089           }
23090           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23091           {
23092             this.field.dom.value = parseInt(v, 10) + increment;
23093             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23094             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23095           }
23096           e.stopEvent();
23097         }
23098     },
23099
23100     // private
23101     beforeLoad : function(){
23102         if(this.loading){
23103             this.loading.disable();
23104         }
23105     },
23106
23107     // private
23108     onClick : function(which){
23109         
23110         var ds = this.ds;
23111         if (!ds) {
23112             return;
23113         }
23114         
23115         switch(which){
23116             case "first":
23117                 ds.load({params:{start: 0, limit: this.pageSize}});
23118             break;
23119             case "prev":
23120                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23121             break;
23122             case "next":
23123                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23124             break;
23125             case "last":
23126                 var total = ds.getTotalCount();
23127                 var extra = total % this.pageSize;
23128                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23129                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23130             break;
23131             case "refresh":
23132                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23133             break;
23134         }
23135     },
23136
23137     /**
23138      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23139      * @param {Roo.data.Store} store The data store to unbind
23140      */
23141     unbind : function(ds){
23142         ds.un("beforeload", this.beforeLoad, this);
23143         ds.un("load", this.onLoad, this);
23144         ds.un("loadexception", this.onLoadError, this);
23145         ds.un("remove", this.updateInfo, this);
23146         ds.un("add", this.updateInfo, this);
23147         this.ds = undefined;
23148     },
23149
23150     /**
23151      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23152      * @param {Roo.data.Store} store The data store to bind
23153      */
23154     bind : function(ds){
23155         ds.on("beforeload", this.beforeLoad, this);
23156         ds.on("load", this.onLoad, this);
23157         ds.on("loadexception", this.onLoadError, this);
23158         ds.on("remove", this.updateInfo, this);
23159         ds.on("add", this.updateInfo, this);
23160         this.ds = ds;
23161     }
23162 });/*
23163  * - LGPL
23164  *
23165  * element
23166  * 
23167  */
23168
23169 /**
23170  * @class Roo.bootstrap.MessageBar
23171  * @extends Roo.bootstrap.Component
23172  * Bootstrap MessageBar class
23173  * @cfg {String} html contents of the MessageBar
23174  * @cfg {String} weight (info | success | warning | danger) default info
23175  * @cfg {String} beforeClass insert the bar before the given class
23176  * @cfg {Boolean} closable (true | false) default false
23177  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23178  * 
23179  * @constructor
23180  * Create a new Element
23181  * @param {Object} config The config object
23182  */
23183
23184 Roo.bootstrap.MessageBar = function(config){
23185     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23186 };
23187
23188 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23189     
23190     html: '',
23191     weight: 'info',
23192     closable: false,
23193     fixed: false,
23194     beforeClass: 'bootstrap-sticky-wrap',
23195     
23196     getAutoCreate : function(){
23197         
23198         var cfg = {
23199             tag: 'div',
23200             cls: 'alert alert-dismissable alert-' + this.weight,
23201             cn: [
23202                 {
23203                     tag: 'span',
23204                     cls: 'message',
23205                     html: this.html || ''
23206                 }
23207             ]
23208         };
23209         
23210         if(this.fixed){
23211             cfg.cls += ' alert-messages-fixed';
23212         }
23213         
23214         if(this.closable){
23215             cfg.cn.push({
23216                 tag: 'button',
23217                 cls: 'close',
23218                 html: 'x'
23219             });
23220         }
23221         
23222         return cfg;
23223     },
23224     
23225     onRender : function(ct, position)
23226     {
23227         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23228         
23229         if(!this.el){
23230             var cfg = Roo.apply({},  this.getAutoCreate());
23231             cfg.id = Roo.id();
23232             
23233             if (this.cls) {
23234                 cfg.cls += ' ' + this.cls;
23235             }
23236             if (this.style) {
23237                 cfg.style = this.style;
23238             }
23239             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23240             
23241             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23242         }
23243         
23244         this.el.select('>button.close').on('click', this.hide, this);
23245         
23246     },
23247     
23248     show : function()
23249     {
23250         if (!this.rendered) {
23251             this.render();
23252         }
23253         
23254         this.el.show();
23255         
23256         this.fireEvent('show', this);
23257         
23258     },
23259     
23260     hide : function()
23261     {
23262         if (!this.rendered) {
23263             this.render();
23264         }
23265         
23266         this.el.hide();
23267         
23268         this.fireEvent('hide', this);
23269     },
23270     
23271     update : function()
23272     {
23273 //        var e = this.el.dom.firstChild;
23274 //        
23275 //        if(this.closable){
23276 //            e = e.nextSibling;
23277 //        }
23278 //        
23279 //        e.data = this.html || '';
23280
23281         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23282     }
23283    
23284 });
23285
23286  
23287
23288      /*
23289  * - LGPL
23290  *
23291  * Graph
23292  * 
23293  */
23294
23295
23296 /**
23297  * @class Roo.bootstrap.Graph
23298  * @extends Roo.bootstrap.Component
23299  * Bootstrap Graph class
23300 > Prameters
23301  -sm {number} sm 4
23302  -md {number} md 5
23303  @cfg {String} graphtype  bar | vbar | pie
23304  @cfg {number} g_x coodinator | centre x (pie)
23305  @cfg {number} g_y coodinator | centre y (pie)
23306  @cfg {number} g_r radius (pie)
23307  @cfg {number} g_height height of the chart (respected by all elements in the set)
23308  @cfg {number} g_width width of the chart (respected by all elements in the set)
23309  @cfg {Object} title The title of the chart
23310     
23311  -{Array}  values
23312  -opts (object) options for the chart 
23313      o {
23314      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23315      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23316      o vgutter (number)
23317      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.
23318      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23319      o to
23320      o stretch (boolean)
23321      o }
23322  -opts (object) options for the pie
23323      o{
23324      o cut
23325      o startAngle (number)
23326      o endAngle (number)
23327      } 
23328  *
23329  * @constructor
23330  * Create a new Input
23331  * @param {Object} config The config object
23332  */
23333
23334 Roo.bootstrap.Graph = function(config){
23335     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23336     
23337     this.addEvents({
23338         // img events
23339         /**
23340          * @event click
23341          * The img click event for the img.
23342          * @param {Roo.EventObject} e
23343          */
23344         "click" : true
23345     });
23346 };
23347
23348 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23349     
23350     sm: 4,
23351     md: 5,
23352     graphtype: 'bar',
23353     g_height: 250,
23354     g_width: 400,
23355     g_x: 50,
23356     g_y: 50,
23357     g_r: 30,
23358     opts:{
23359         //g_colors: this.colors,
23360         g_type: 'soft',
23361         g_gutter: '20%'
23362
23363     },
23364     title : false,
23365
23366     getAutoCreate : function(){
23367         
23368         var cfg = {
23369             tag: 'div',
23370             html : null
23371         };
23372         
23373         
23374         return  cfg;
23375     },
23376
23377     onRender : function(ct,position){
23378         
23379         
23380         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23381         
23382         if (typeof(Raphael) == 'undefined') {
23383             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23384             return;
23385         }
23386         
23387         this.raphael = Raphael(this.el.dom);
23388         
23389                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23390                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23391                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23392                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23393                 /*
23394                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23395                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23396                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23397                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23398                 
23399                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23400                 r.barchart(330, 10, 300, 220, data1);
23401                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23402                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23403                 */
23404                 
23405                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23406                 // r.barchart(30, 30, 560, 250,  xdata, {
23407                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23408                 //     axis : "0 0 1 1",
23409                 //     axisxlabels :  xdata
23410                 //     //yvalues : cols,
23411                    
23412                 // });
23413 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23414 //        
23415 //        this.load(null,xdata,{
23416 //                axis : "0 0 1 1",
23417 //                axisxlabels :  xdata
23418 //                });
23419
23420     },
23421
23422     load : function(graphtype,xdata,opts)
23423     {
23424         this.raphael.clear();
23425         if(!graphtype) {
23426             graphtype = this.graphtype;
23427         }
23428         if(!opts){
23429             opts = this.opts;
23430         }
23431         var r = this.raphael,
23432             fin = function () {
23433                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23434             },
23435             fout = function () {
23436                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23437             },
23438             pfin = function() {
23439                 this.sector.stop();
23440                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23441
23442                 if (this.label) {
23443                     this.label[0].stop();
23444                     this.label[0].attr({ r: 7.5 });
23445                     this.label[1].attr({ "font-weight": 800 });
23446                 }
23447             },
23448             pfout = function() {
23449                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23450
23451                 if (this.label) {
23452                     this.label[0].animate({ r: 5 }, 500, "bounce");
23453                     this.label[1].attr({ "font-weight": 400 });
23454                 }
23455             };
23456
23457         switch(graphtype){
23458             case 'bar':
23459                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23460                 break;
23461             case 'hbar':
23462                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23463                 break;
23464             case 'pie':
23465 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23466 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23467 //            
23468                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23469                 
23470                 break;
23471
23472         }
23473         
23474         if(this.title){
23475             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23476         }
23477         
23478     },
23479     
23480     setTitle: function(o)
23481     {
23482         this.title = o;
23483     },
23484     
23485     initEvents: function() {
23486         
23487         if(!this.href){
23488             this.el.on('click', this.onClick, this);
23489         }
23490     },
23491     
23492     onClick : function(e)
23493     {
23494         Roo.log('img onclick');
23495         this.fireEvent('click', this, e);
23496     }
23497    
23498 });
23499
23500  
23501 /*
23502  * - LGPL
23503  *
23504  * numberBox
23505  * 
23506  */
23507 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23508
23509 /**
23510  * @class Roo.bootstrap.dash.NumberBox
23511  * @extends Roo.bootstrap.Component
23512  * Bootstrap NumberBox class
23513  * @cfg {String} headline Box headline
23514  * @cfg {String} content Box content
23515  * @cfg {String} icon Box icon
23516  * @cfg {String} footer Footer text
23517  * @cfg {String} fhref Footer href
23518  * 
23519  * @constructor
23520  * Create a new NumberBox
23521  * @param {Object} config The config object
23522  */
23523
23524
23525 Roo.bootstrap.dash.NumberBox = function(config){
23526     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23527     
23528 };
23529
23530 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23531     
23532     headline : '',
23533     content : '',
23534     icon : '',
23535     footer : '',
23536     fhref : '',
23537     ficon : '',
23538     
23539     getAutoCreate : function(){
23540         
23541         var cfg = {
23542             tag : 'div',
23543             cls : 'small-box ',
23544             cn : [
23545                 {
23546                     tag : 'div',
23547                     cls : 'inner',
23548                     cn :[
23549                         {
23550                             tag : 'h3',
23551                             cls : 'roo-headline',
23552                             html : this.headline
23553                         },
23554                         {
23555                             tag : 'p',
23556                             cls : 'roo-content',
23557                             html : this.content
23558                         }
23559                     ]
23560                 }
23561             ]
23562         };
23563         
23564         if(this.icon){
23565             cfg.cn.push({
23566                 tag : 'div',
23567                 cls : 'icon',
23568                 cn :[
23569                     {
23570                         tag : 'i',
23571                         cls : 'ion ' + this.icon
23572                     }
23573                 ]
23574             });
23575         }
23576         
23577         if(this.footer){
23578             var footer = {
23579                 tag : 'a',
23580                 cls : 'small-box-footer',
23581                 href : this.fhref || '#',
23582                 html : this.footer
23583             };
23584             
23585             cfg.cn.push(footer);
23586             
23587         }
23588         
23589         return  cfg;
23590     },
23591
23592     onRender : function(ct,position){
23593         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23594
23595
23596        
23597                 
23598     },
23599
23600     setHeadline: function (value)
23601     {
23602         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23603     },
23604     
23605     setFooter: function (value, href)
23606     {
23607         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23608         
23609         if(href){
23610             this.el.select('a.small-box-footer',true).first().attr('href', href);
23611         }
23612         
23613     },
23614
23615     setContent: function (value)
23616     {
23617         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23618     },
23619
23620     initEvents: function() 
23621     {   
23622         
23623     }
23624     
23625 });
23626
23627  
23628 /*
23629  * - LGPL
23630  *
23631  * TabBox
23632  * 
23633  */
23634 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23635
23636 /**
23637  * @class Roo.bootstrap.dash.TabBox
23638  * @extends Roo.bootstrap.Component
23639  * Bootstrap TabBox class
23640  * @cfg {String} title Title of the TabBox
23641  * @cfg {String} icon Icon of the TabBox
23642  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23643  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23644  * 
23645  * @constructor
23646  * Create a new TabBox
23647  * @param {Object} config The config object
23648  */
23649
23650
23651 Roo.bootstrap.dash.TabBox = function(config){
23652     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23653     this.addEvents({
23654         // raw events
23655         /**
23656          * @event addpane
23657          * When a pane is added
23658          * @param {Roo.bootstrap.dash.TabPane} pane
23659          */
23660         "addpane" : true,
23661         /**
23662          * @event activatepane
23663          * When a pane is activated
23664          * @param {Roo.bootstrap.dash.TabPane} pane
23665          */
23666         "activatepane" : true
23667         
23668          
23669     });
23670     
23671     this.panes = [];
23672 };
23673
23674 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23675
23676     title : '',
23677     icon : false,
23678     showtabs : true,
23679     tabScrollable : false,
23680     
23681     getChildContainer : function()
23682     {
23683         return this.el.select('.tab-content', true).first();
23684     },
23685     
23686     getAutoCreate : function(){
23687         
23688         var header = {
23689             tag: 'li',
23690             cls: 'pull-left header',
23691             html: this.title,
23692             cn : []
23693         };
23694         
23695         if(this.icon){
23696             header.cn.push({
23697                 tag: 'i',
23698                 cls: 'fa ' + this.icon
23699             });
23700         }
23701         
23702         var h = {
23703             tag: 'ul',
23704             cls: 'nav nav-tabs pull-right',
23705             cn: [
23706                 header
23707             ]
23708         };
23709         
23710         if(this.tabScrollable){
23711             h = {
23712                 tag: 'div',
23713                 cls: 'tab-header',
23714                 cn: [
23715                     {
23716                         tag: 'ul',
23717                         cls: 'nav nav-tabs pull-right',
23718                         cn: [
23719                             header
23720                         ]
23721                     }
23722                 ]
23723             };
23724         }
23725         
23726         var cfg = {
23727             tag: 'div',
23728             cls: 'nav-tabs-custom',
23729             cn: [
23730                 h,
23731                 {
23732                     tag: 'div',
23733                     cls: 'tab-content no-padding',
23734                     cn: []
23735                 }
23736             ]
23737         };
23738
23739         return  cfg;
23740     },
23741     initEvents : function()
23742     {
23743         //Roo.log('add add pane handler');
23744         this.on('addpane', this.onAddPane, this);
23745     },
23746      /**
23747      * Updates the box title
23748      * @param {String} html to set the title to.
23749      */
23750     setTitle : function(value)
23751     {
23752         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23753     },
23754     onAddPane : function(pane)
23755     {
23756         this.panes.push(pane);
23757         //Roo.log('addpane');
23758         //Roo.log(pane);
23759         // tabs are rendere left to right..
23760         if(!this.showtabs){
23761             return;
23762         }
23763         
23764         var ctr = this.el.select('.nav-tabs', true).first();
23765          
23766          
23767         var existing = ctr.select('.nav-tab',true);
23768         var qty = existing.getCount();;
23769         
23770         
23771         var tab = ctr.createChild({
23772             tag : 'li',
23773             cls : 'nav-tab' + (qty ? '' : ' active'),
23774             cn : [
23775                 {
23776                     tag : 'a',
23777                     href:'#',
23778                     html : pane.title
23779                 }
23780             ]
23781         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23782         pane.tab = tab;
23783         
23784         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23785         if (!qty) {
23786             pane.el.addClass('active');
23787         }
23788         
23789                 
23790     },
23791     onTabClick : function(ev,un,ob,pane)
23792     {
23793         //Roo.log('tab - prev default');
23794         ev.preventDefault();
23795         
23796         
23797         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23798         pane.tab.addClass('active');
23799         //Roo.log(pane.title);
23800         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23801         // technically we should have a deactivate event.. but maybe add later.
23802         // and it should not de-activate the selected tab...
23803         this.fireEvent('activatepane', pane);
23804         pane.el.addClass('active');
23805         pane.fireEvent('activate');
23806         
23807         
23808     },
23809     
23810     getActivePane : function()
23811     {
23812         var r = false;
23813         Roo.each(this.panes, function(p) {
23814             if(p.el.hasClass('active')){
23815                 r = p;
23816                 return false;
23817             }
23818             
23819             return;
23820         });
23821         
23822         return r;
23823     }
23824     
23825     
23826 });
23827
23828  
23829 /*
23830  * - LGPL
23831  *
23832  * Tab pane
23833  * 
23834  */
23835 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23836 /**
23837  * @class Roo.bootstrap.TabPane
23838  * @extends Roo.bootstrap.Component
23839  * Bootstrap TabPane class
23840  * @cfg {Boolean} active (false | true) Default false
23841  * @cfg {String} title title of panel
23842
23843  * 
23844  * @constructor
23845  * Create a new TabPane
23846  * @param {Object} config The config object
23847  */
23848
23849 Roo.bootstrap.dash.TabPane = function(config){
23850     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23851     
23852     this.addEvents({
23853         // raw events
23854         /**
23855          * @event activate
23856          * When a pane is activated
23857          * @param {Roo.bootstrap.dash.TabPane} pane
23858          */
23859         "activate" : true
23860          
23861     });
23862 };
23863
23864 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23865     
23866     active : false,
23867     title : '',
23868     
23869     // the tabBox that this is attached to.
23870     tab : false,
23871      
23872     getAutoCreate : function() 
23873     {
23874         var cfg = {
23875             tag: 'div',
23876             cls: 'tab-pane'
23877         };
23878         
23879         if(this.active){
23880             cfg.cls += ' active';
23881         }
23882         
23883         return cfg;
23884     },
23885     initEvents  : function()
23886     {
23887         //Roo.log('trigger add pane handler');
23888         this.parent().fireEvent('addpane', this)
23889     },
23890     
23891      /**
23892      * Updates the tab title 
23893      * @param {String} html to set the title to.
23894      */
23895     setTitle: function(str)
23896     {
23897         if (!this.tab) {
23898             return;
23899         }
23900         this.title = str;
23901         this.tab.select('a', true).first().dom.innerHTML = str;
23902         
23903     }
23904     
23905     
23906     
23907 });
23908
23909  
23910
23911
23912  /*
23913  * - LGPL
23914  *
23915  * menu
23916  * 
23917  */
23918 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23919
23920 /**
23921  * @class Roo.bootstrap.menu.Menu
23922  * @extends Roo.bootstrap.Component
23923  * Bootstrap Menu class - container for Menu
23924  * @cfg {String} html Text of the menu
23925  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23926  * @cfg {String} icon Font awesome icon
23927  * @cfg {String} pos Menu align to (top | bottom) default bottom
23928  * 
23929  * 
23930  * @constructor
23931  * Create a new Menu
23932  * @param {Object} config The config object
23933  */
23934
23935
23936 Roo.bootstrap.menu.Menu = function(config){
23937     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23938     
23939     this.addEvents({
23940         /**
23941          * @event beforeshow
23942          * Fires before this menu is displayed
23943          * @param {Roo.bootstrap.menu.Menu} this
23944          */
23945         beforeshow : true,
23946         /**
23947          * @event beforehide
23948          * Fires before this menu is hidden
23949          * @param {Roo.bootstrap.menu.Menu} this
23950          */
23951         beforehide : true,
23952         /**
23953          * @event show
23954          * Fires after this menu is displayed
23955          * @param {Roo.bootstrap.menu.Menu} this
23956          */
23957         show : true,
23958         /**
23959          * @event hide
23960          * Fires after this menu is hidden
23961          * @param {Roo.bootstrap.menu.Menu} this
23962          */
23963         hide : true,
23964         /**
23965          * @event click
23966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23967          * @param {Roo.bootstrap.menu.Menu} this
23968          * @param {Roo.EventObject} e
23969          */
23970         click : true
23971     });
23972     
23973 };
23974
23975 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23976     
23977     submenu : false,
23978     html : '',
23979     weight : 'default',
23980     icon : false,
23981     pos : 'bottom',
23982     
23983     
23984     getChildContainer : function() {
23985         if(this.isSubMenu){
23986             return this.el;
23987         }
23988         
23989         return this.el.select('ul.dropdown-menu', true).first();  
23990     },
23991     
23992     getAutoCreate : function()
23993     {
23994         var text = [
23995             {
23996                 tag : 'span',
23997                 cls : 'roo-menu-text',
23998                 html : this.html
23999             }
24000         ];
24001         
24002         if(this.icon){
24003             text.unshift({
24004                 tag : 'i',
24005                 cls : 'fa ' + this.icon
24006             })
24007         }
24008         
24009         
24010         var cfg = {
24011             tag : 'div',
24012             cls : 'btn-group',
24013             cn : [
24014                 {
24015                     tag : 'button',
24016                     cls : 'dropdown-button btn btn-' + this.weight,
24017                     cn : text
24018                 },
24019                 {
24020                     tag : 'button',
24021                     cls : 'dropdown-toggle btn btn-' + this.weight,
24022                     cn : [
24023                         {
24024                             tag : 'span',
24025                             cls : 'caret'
24026                         }
24027                     ]
24028                 },
24029                 {
24030                     tag : 'ul',
24031                     cls : 'dropdown-menu'
24032                 }
24033             ]
24034             
24035         };
24036         
24037         if(this.pos == 'top'){
24038             cfg.cls += ' dropup';
24039         }
24040         
24041         if(this.isSubMenu){
24042             cfg = {
24043                 tag : 'ul',
24044                 cls : 'dropdown-menu'
24045             }
24046         }
24047         
24048         return cfg;
24049     },
24050     
24051     onRender : function(ct, position)
24052     {
24053         this.isSubMenu = ct.hasClass('dropdown-submenu');
24054         
24055         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24056     },
24057     
24058     initEvents : function() 
24059     {
24060         if(this.isSubMenu){
24061             return;
24062         }
24063         
24064         this.hidden = true;
24065         
24066         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24067         this.triggerEl.on('click', this.onTriggerPress, this);
24068         
24069         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24070         this.buttonEl.on('click', this.onClick, this);
24071         
24072     },
24073     
24074     list : function()
24075     {
24076         if(this.isSubMenu){
24077             return this.el;
24078         }
24079         
24080         return this.el.select('ul.dropdown-menu', true).first();
24081     },
24082     
24083     onClick : function(e)
24084     {
24085         this.fireEvent("click", this, e);
24086     },
24087     
24088     onTriggerPress  : function(e)
24089     {   
24090         if (this.isVisible()) {
24091             this.hide();
24092         } else {
24093             this.show();
24094         }
24095     },
24096     
24097     isVisible : function(){
24098         return !this.hidden;
24099     },
24100     
24101     show : function()
24102     {
24103         this.fireEvent("beforeshow", this);
24104         
24105         this.hidden = false;
24106         this.el.addClass('open');
24107         
24108         Roo.get(document).on("mouseup", this.onMouseUp, this);
24109         
24110         this.fireEvent("show", this);
24111         
24112         
24113     },
24114     
24115     hide : function()
24116     {
24117         this.fireEvent("beforehide", this);
24118         
24119         this.hidden = true;
24120         this.el.removeClass('open');
24121         
24122         Roo.get(document).un("mouseup", this.onMouseUp);
24123         
24124         this.fireEvent("hide", this);
24125     },
24126     
24127     onMouseUp : function()
24128     {
24129         this.hide();
24130     }
24131     
24132 });
24133
24134  
24135  /*
24136  * - LGPL
24137  *
24138  * menu item
24139  * 
24140  */
24141 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24142
24143 /**
24144  * @class Roo.bootstrap.menu.Item
24145  * @extends Roo.bootstrap.Component
24146  * Bootstrap MenuItem class
24147  * @cfg {Boolean} submenu (true | false) default false
24148  * @cfg {String} html text of the item
24149  * @cfg {String} href the link
24150  * @cfg {Boolean} disable (true | false) default false
24151  * @cfg {Boolean} preventDefault (true | false) default true
24152  * @cfg {String} icon Font awesome icon
24153  * @cfg {String} pos Submenu align to (left | right) default right 
24154  * 
24155  * 
24156  * @constructor
24157  * Create a new Item
24158  * @param {Object} config The config object
24159  */
24160
24161
24162 Roo.bootstrap.menu.Item = function(config){
24163     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24164     this.addEvents({
24165         /**
24166          * @event mouseover
24167          * Fires when the mouse is hovering over this menu
24168          * @param {Roo.bootstrap.menu.Item} this
24169          * @param {Roo.EventObject} e
24170          */
24171         mouseover : true,
24172         /**
24173          * @event mouseout
24174          * Fires when the mouse exits this menu
24175          * @param {Roo.bootstrap.menu.Item} this
24176          * @param {Roo.EventObject} e
24177          */
24178         mouseout : true,
24179         // raw events
24180         /**
24181          * @event click
24182          * The raw click event for the entire grid.
24183          * @param {Roo.EventObject} e
24184          */
24185         click : true
24186     });
24187 };
24188
24189 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24190     
24191     submenu : false,
24192     href : '',
24193     html : '',
24194     preventDefault: true,
24195     disable : false,
24196     icon : false,
24197     pos : 'right',
24198     
24199     getAutoCreate : function()
24200     {
24201         var text = [
24202             {
24203                 tag : 'span',
24204                 cls : 'roo-menu-item-text',
24205                 html : this.html
24206             }
24207         ];
24208         
24209         if(this.icon){
24210             text.unshift({
24211                 tag : 'i',
24212                 cls : 'fa ' + this.icon
24213             })
24214         }
24215         
24216         var cfg = {
24217             tag : 'li',
24218             cn : [
24219                 {
24220                     tag : 'a',
24221                     href : this.href || '#',
24222                     cn : text
24223                 }
24224             ]
24225         };
24226         
24227         if(this.disable){
24228             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24229         }
24230         
24231         if(this.submenu){
24232             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24233             
24234             if(this.pos == 'left'){
24235                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24236             }
24237         }
24238         
24239         return cfg;
24240     },
24241     
24242     initEvents : function() 
24243     {
24244         this.el.on('mouseover', this.onMouseOver, this);
24245         this.el.on('mouseout', this.onMouseOut, this);
24246         
24247         this.el.select('a', true).first().on('click', this.onClick, this);
24248         
24249     },
24250     
24251     onClick : function(e)
24252     {
24253         if(this.preventDefault){
24254             e.preventDefault();
24255         }
24256         
24257         this.fireEvent("click", this, e);
24258     },
24259     
24260     onMouseOver : function(e)
24261     {
24262         if(this.submenu && this.pos == 'left'){
24263             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24264         }
24265         
24266         this.fireEvent("mouseover", this, e);
24267     },
24268     
24269     onMouseOut : function(e)
24270     {
24271         this.fireEvent("mouseout", this, e);
24272     }
24273 });
24274
24275  
24276
24277  /*
24278  * - LGPL
24279  *
24280  * menu separator
24281  * 
24282  */
24283 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24284
24285 /**
24286  * @class Roo.bootstrap.menu.Separator
24287  * @extends Roo.bootstrap.Component
24288  * Bootstrap Separator class
24289  * 
24290  * @constructor
24291  * Create a new Separator
24292  * @param {Object} config The config object
24293  */
24294
24295
24296 Roo.bootstrap.menu.Separator = function(config){
24297     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24298 };
24299
24300 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24301     
24302     getAutoCreate : function(){
24303         var cfg = {
24304             tag : 'li',
24305             cls: 'divider'
24306         };
24307         
24308         return cfg;
24309     }
24310    
24311 });
24312
24313  
24314
24315  /*
24316  * - LGPL
24317  *
24318  * Tooltip
24319  * 
24320  */
24321
24322 /**
24323  * @class Roo.bootstrap.Tooltip
24324  * Bootstrap Tooltip class
24325  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24326  * to determine which dom element triggers the tooltip.
24327  * 
24328  * It needs to add support for additional attributes like tooltip-position
24329  * 
24330  * @constructor
24331  * Create a new Toolti
24332  * @param {Object} config The config object
24333  */
24334
24335 Roo.bootstrap.Tooltip = function(config){
24336     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24337 };
24338
24339 Roo.apply(Roo.bootstrap.Tooltip, {
24340     /**
24341      * @function init initialize tooltip monitoring.
24342      * @static
24343      */
24344     currentEl : false,
24345     currentTip : false,
24346     currentRegion : false,
24347     
24348     //  init : delay?
24349     
24350     init : function()
24351     {
24352         Roo.get(document).on('mouseover', this.enter ,this);
24353         Roo.get(document).on('mouseout', this.leave, this);
24354          
24355         
24356         this.currentTip = new Roo.bootstrap.Tooltip();
24357     },
24358     
24359     enter : function(ev)
24360     {
24361         var dom = ev.getTarget();
24362         
24363         //Roo.log(['enter',dom]);
24364         var el = Roo.fly(dom);
24365         if (this.currentEl) {
24366             //Roo.log(dom);
24367             //Roo.log(this.currentEl);
24368             //Roo.log(this.currentEl.contains(dom));
24369             if (this.currentEl == el) {
24370                 return;
24371             }
24372             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24373                 return;
24374             }
24375
24376         }
24377         
24378         if (this.currentTip.el) {
24379             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24380         }    
24381         //Roo.log(ev);
24382         
24383         if(!el || el.dom == document){
24384             return;
24385         }
24386         
24387         var bindEl = el;
24388         
24389         // you can not look for children, as if el is the body.. then everythign is the child..
24390         if (!el.attr('tooltip')) { //
24391             if (!el.select("[tooltip]").elements.length) {
24392                 return;
24393             }
24394             // is the mouse over this child...?
24395             bindEl = el.select("[tooltip]").first();
24396             var xy = ev.getXY();
24397             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24398                 //Roo.log("not in region.");
24399                 return;
24400             }
24401             //Roo.log("child element over..");
24402             
24403         }
24404         this.currentEl = bindEl;
24405         this.currentTip.bind(bindEl);
24406         this.currentRegion = Roo.lib.Region.getRegion(dom);
24407         this.currentTip.enter();
24408         
24409     },
24410     leave : function(ev)
24411     {
24412         var dom = ev.getTarget();
24413         //Roo.log(['leave',dom]);
24414         if (!this.currentEl) {
24415             return;
24416         }
24417         
24418         
24419         if (dom != this.currentEl.dom) {
24420             return;
24421         }
24422         var xy = ev.getXY();
24423         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24424             return;
24425         }
24426         // only activate leave if mouse cursor is outside... bounding box..
24427         
24428         
24429         
24430         
24431         if (this.currentTip) {
24432             this.currentTip.leave();
24433         }
24434         //Roo.log('clear currentEl');
24435         this.currentEl = false;
24436         
24437         
24438     },
24439     alignment : {
24440         'left' : ['r-l', [-2,0], 'right'],
24441         'right' : ['l-r', [2,0], 'left'],
24442         'bottom' : ['t-b', [0,2], 'top'],
24443         'top' : [ 'b-t', [0,-2], 'bottom']
24444     }
24445     
24446 });
24447
24448
24449 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24450     
24451     
24452     bindEl : false,
24453     
24454     delay : null, // can be { show : 300 , hide: 500}
24455     
24456     timeout : null,
24457     
24458     hoverState : null, //???
24459     
24460     placement : 'bottom', 
24461     
24462     getAutoCreate : function(){
24463     
24464         var cfg = {
24465            cls : 'tooltip',
24466            role : 'tooltip',
24467            cn : [
24468                 {
24469                     cls : 'tooltip-arrow'
24470                 },
24471                 {
24472                     cls : 'tooltip-inner'
24473                 }
24474            ]
24475         };
24476         
24477         return cfg;
24478     },
24479     bind : function(el)
24480     {
24481         this.bindEl = el;
24482     },
24483       
24484     
24485     enter : function () {
24486        
24487         if (this.timeout != null) {
24488             clearTimeout(this.timeout);
24489         }
24490         
24491         this.hoverState = 'in';
24492          //Roo.log("enter - show");
24493         if (!this.delay || !this.delay.show) {
24494             this.show();
24495             return;
24496         }
24497         var _t = this;
24498         this.timeout = setTimeout(function () {
24499             if (_t.hoverState == 'in') {
24500                 _t.show();
24501             }
24502         }, this.delay.show);
24503     },
24504     leave : function()
24505     {
24506         clearTimeout(this.timeout);
24507     
24508         this.hoverState = 'out';
24509          if (!this.delay || !this.delay.hide) {
24510             this.hide();
24511             return;
24512         }
24513        
24514         var _t = this;
24515         this.timeout = setTimeout(function () {
24516             //Roo.log("leave - timeout");
24517             
24518             if (_t.hoverState == 'out') {
24519                 _t.hide();
24520                 Roo.bootstrap.Tooltip.currentEl = false;
24521             }
24522         }, delay);
24523     },
24524     
24525     show : function ()
24526     {
24527         if (!this.el) {
24528             this.render(document.body);
24529         }
24530         // set content.
24531         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24532         
24533         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24534         
24535         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24536         
24537         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24538         
24539         var placement = typeof this.placement == 'function' ?
24540             this.placement.call(this, this.el, on_el) :
24541             this.placement;
24542             
24543         var autoToken = /\s?auto?\s?/i;
24544         var autoPlace = autoToken.test(placement);
24545         if (autoPlace) {
24546             placement = placement.replace(autoToken, '') || 'top';
24547         }
24548         
24549         //this.el.detach()
24550         //this.el.setXY([0,0]);
24551         this.el.show();
24552         //this.el.dom.style.display='block';
24553         
24554         //this.el.appendTo(on_el);
24555         
24556         var p = this.getPosition();
24557         var box = this.el.getBox();
24558         
24559         if (autoPlace) {
24560             // fixme..
24561         }
24562         
24563         var align = Roo.bootstrap.Tooltip.alignment[placement];
24564         
24565         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24566         
24567         if(placement == 'top' || placement == 'bottom'){
24568             if(xy[0] < 0){
24569                 placement = 'right';
24570             }
24571             
24572             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24573                 placement = 'left';
24574             }
24575             
24576             var scroll = Roo.select('body', true).first().getScroll();
24577             
24578             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24579                 placement = 'top';
24580             }
24581             
24582         }
24583         
24584         align = Roo.bootstrap.Tooltip.alignment[placement];
24585         
24586         this.el.alignTo(this.bindEl, align[0],align[1]);
24587         //var arrow = this.el.select('.arrow',true).first();
24588         //arrow.set(align[2], 
24589         
24590         this.el.addClass(placement);
24591         
24592         this.el.addClass('in fade');
24593         
24594         this.hoverState = null;
24595         
24596         if (this.el.hasClass('fade')) {
24597             // fade it?
24598         }
24599         
24600     },
24601     hide : function()
24602     {
24603          
24604         if (!this.el) {
24605             return;
24606         }
24607         //this.el.setXY([0,0]);
24608         this.el.removeClass('in');
24609         //this.el.hide();
24610         
24611     }
24612     
24613 });
24614  
24615
24616  /*
24617  * - LGPL
24618  *
24619  * Location Picker
24620  * 
24621  */
24622
24623 /**
24624  * @class Roo.bootstrap.LocationPicker
24625  * @extends Roo.bootstrap.Component
24626  * Bootstrap LocationPicker class
24627  * @cfg {Number} latitude Position when init default 0
24628  * @cfg {Number} longitude Position when init default 0
24629  * @cfg {Number} zoom default 15
24630  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24631  * @cfg {Boolean} mapTypeControl default false
24632  * @cfg {Boolean} disableDoubleClickZoom default false
24633  * @cfg {Boolean} scrollwheel default true
24634  * @cfg {Boolean} streetViewControl default false
24635  * @cfg {Number} radius default 0
24636  * @cfg {String} locationName
24637  * @cfg {Boolean} draggable default true
24638  * @cfg {Boolean} enableAutocomplete default false
24639  * @cfg {Boolean} enableReverseGeocode default true
24640  * @cfg {String} markerTitle
24641  * 
24642  * @constructor
24643  * Create a new LocationPicker
24644  * @param {Object} config The config object
24645  */
24646
24647
24648 Roo.bootstrap.LocationPicker = function(config){
24649     
24650     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24651     
24652     this.addEvents({
24653         /**
24654          * @event initial
24655          * Fires when the picker initialized.
24656          * @param {Roo.bootstrap.LocationPicker} this
24657          * @param {Google Location} location
24658          */
24659         initial : true,
24660         /**
24661          * @event positionchanged
24662          * Fires when the picker position changed.
24663          * @param {Roo.bootstrap.LocationPicker} this
24664          * @param {Google Location} location
24665          */
24666         positionchanged : true,
24667         /**
24668          * @event resize
24669          * Fires when the map resize.
24670          * @param {Roo.bootstrap.LocationPicker} this
24671          */
24672         resize : true,
24673         /**
24674          * @event show
24675          * Fires when the map show.
24676          * @param {Roo.bootstrap.LocationPicker} this
24677          */
24678         show : true,
24679         /**
24680          * @event hide
24681          * Fires when the map hide.
24682          * @param {Roo.bootstrap.LocationPicker} this
24683          */
24684         hide : true,
24685         /**
24686          * @event mapClick
24687          * Fires when click the map.
24688          * @param {Roo.bootstrap.LocationPicker} this
24689          * @param {Map event} e
24690          */
24691         mapClick : true,
24692         /**
24693          * @event mapRightClick
24694          * Fires when right click the map.
24695          * @param {Roo.bootstrap.LocationPicker} this
24696          * @param {Map event} e
24697          */
24698         mapRightClick : true,
24699         /**
24700          * @event markerClick
24701          * Fires when click the marker.
24702          * @param {Roo.bootstrap.LocationPicker} this
24703          * @param {Map event} e
24704          */
24705         markerClick : true,
24706         /**
24707          * @event markerRightClick
24708          * Fires when right click the marker.
24709          * @param {Roo.bootstrap.LocationPicker} this
24710          * @param {Map event} e
24711          */
24712         markerRightClick : true,
24713         /**
24714          * @event OverlayViewDraw
24715          * Fires when OverlayView Draw
24716          * @param {Roo.bootstrap.LocationPicker} this
24717          */
24718         OverlayViewDraw : true,
24719         /**
24720          * @event OverlayViewOnAdd
24721          * Fires when OverlayView Draw
24722          * @param {Roo.bootstrap.LocationPicker} this
24723          */
24724         OverlayViewOnAdd : true,
24725         /**
24726          * @event OverlayViewOnRemove
24727          * Fires when OverlayView Draw
24728          * @param {Roo.bootstrap.LocationPicker} this
24729          */
24730         OverlayViewOnRemove : true,
24731         /**
24732          * @event OverlayViewShow
24733          * Fires when OverlayView Draw
24734          * @param {Roo.bootstrap.LocationPicker} this
24735          * @param {Pixel} cpx
24736          */
24737         OverlayViewShow : true,
24738         /**
24739          * @event OverlayViewHide
24740          * Fires when OverlayView Draw
24741          * @param {Roo.bootstrap.LocationPicker} this
24742          */
24743         OverlayViewHide : true,
24744         /**
24745          * @event loadexception
24746          * Fires when load google lib failed.
24747          * @param {Roo.bootstrap.LocationPicker} this
24748          */
24749         loadexception : true
24750     });
24751         
24752 };
24753
24754 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24755     
24756     gMapContext: false,
24757     
24758     latitude: 0,
24759     longitude: 0,
24760     zoom: 15,
24761     mapTypeId: false,
24762     mapTypeControl: false,
24763     disableDoubleClickZoom: false,
24764     scrollwheel: true,
24765     streetViewControl: false,
24766     radius: 0,
24767     locationName: '',
24768     draggable: true,
24769     enableAutocomplete: false,
24770     enableReverseGeocode: true,
24771     markerTitle: '',
24772     
24773     getAutoCreate: function()
24774     {
24775
24776         var cfg = {
24777             tag: 'div',
24778             cls: 'roo-location-picker'
24779         };
24780         
24781         return cfg
24782     },
24783     
24784     initEvents: function(ct, position)
24785     {       
24786         if(!this.el.getWidth() || this.isApplied()){
24787             return;
24788         }
24789         
24790         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24791         
24792         this.initial();
24793     },
24794     
24795     initial: function()
24796     {
24797         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24798             this.fireEvent('loadexception', this);
24799             return;
24800         }
24801         
24802         if(!this.mapTypeId){
24803             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24804         }
24805         
24806         this.gMapContext = this.GMapContext();
24807         
24808         this.initOverlayView();
24809         
24810         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24811         
24812         var _this = this;
24813                 
24814         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24815             _this.setPosition(_this.gMapContext.marker.position);
24816         });
24817         
24818         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24819             _this.fireEvent('mapClick', this, event);
24820             
24821         });
24822
24823         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24824             _this.fireEvent('mapRightClick', this, event);
24825             
24826         });
24827         
24828         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24829             _this.fireEvent('markerClick', this, event);
24830             
24831         });
24832
24833         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24834             _this.fireEvent('markerRightClick', this, event);
24835             
24836         });
24837         
24838         this.setPosition(this.gMapContext.location);
24839         
24840         this.fireEvent('initial', this, this.gMapContext.location);
24841     },
24842     
24843     initOverlayView: function()
24844     {
24845         var _this = this;
24846         
24847         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24848             
24849             draw: function()
24850             {
24851                 _this.fireEvent('OverlayViewDraw', _this);
24852             },
24853             
24854             onAdd: function()
24855             {
24856                 _this.fireEvent('OverlayViewOnAdd', _this);
24857             },
24858             
24859             onRemove: function()
24860             {
24861                 _this.fireEvent('OverlayViewOnRemove', _this);
24862             },
24863             
24864             show: function(cpx)
24865             {
24866                 _this.fireEvent('OverlayViewShow', _this, cpx);
24867             },
24868             
24869             hide: function()
24870             {
24871                 _this.fireEvent('OverlayViewHide', _this);
24872             }
24873             
24874         });
24875     },
24876     
24877     fromLatLngToContainerPixel: function(event)
24878     {
24879         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24880     },
24881     
24882     isApplied: function() 
24883     {
24884         return this.getGmapContext() == false ? false : true;
24885     },
24886     
24887     getGmapContext: function() 
24888     {
24889         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24890     },
24891     
24892     GMapContext: function() 
24893     {
24894         var position = new google.maps.LatLng(this.latitude, this.longitude);
24895         
24896         var _map = new google.maps.Map(this.el.dom, {
24897             center: position,
24898             zoom: this.zoom,
24899             mapTypeId: this.mapTypeId,
24900             mapTypeControl: this.mapTypeControl,
24901             disableDoubleClickZoom: this.disableDoubleClickZoom,
24902             scrollwheel: this.scrollwheel,
24903             streetViewControl: this.streetViewControl,
24904             locationName: this.locationName,
24905             draggable: this.draggable,
24906             enableAutocomplete: this.enableAutocomplete,
24907             enableReverseGeocode: this.enableReverseGeocode
24908         });
24909         
24910         var _marker = new google.maps.Marker({
24911             position: position,
24912             map: _map,
24913             title: this.markerTitle,
24914             draggable: this.draggable
24915         });
24916         
24917         return {
24918             map: _map,
24919             marker: _marker,
24920             circle: null,
24921             location: position,
24922             radius: this.radius,
24923             locationName: this.locationName,
24924             addressComponents: {
24925                 formatted_address: null,
24926                 addressLine1: null,
24927                 addressLine2: null,
24928                 streetName: null,
24929                 streetNumber: null,
24930                 city: null,
24931                 district: null,
24932                 state: null,
24933                 stateOrProvince: null
24934             },
24935             settings: this,
24936             domContainer: this.el.dom,
24937             geodecoder: new google.maps.Geocoder()
24938         };
24939     },
24940     
24941     drawCircle: function(center, radius, options) 
24942     {
24943         if (this.gMapContext.circle != null) {
24944             this.gMapContext.circle.setMap(null);
24945         }
24946         if (radius > 0) {
24947             radius *= 1;
24948             options = Roo.apply({}, options, {
24949                 strokeColor: "#0000FF",
24950                 strokeOpacity: .35,
24951                 strokeWeight: 2,
24952                 fillColor: "#0000FF",
24953                 fillOpacity: .2
24954             });
24955             
24956             options.map = this.gMapContext.map;
24957             options.radius = radius;
24958             options.center = center;
24959             this.gMapContext.circle = new google.maps.Circle(options);
24960             return this.gMapContext.circle;
24961         }
24962         
24963         return null;
24964     },
24965     
24966     setPosition: function(location) 
24967     {
24968         this.gMapContext.location = location;
24969         this.gMapContext.marker.setPosition(location);
24970         this.gMapContext.map.panTo(location);
24971         this.drawCircle(location, this.gMapContext.radius, {});
24972         
24973         var _this = this;
24974         
24975         if (this.gMapContext.settings.enableReverseGeocode) {
24976             this.gMapContext.geodecoder.geocode({
24977                 latLng: this.gMapContext.location
24978             }, function(results, status) {
24979                 
24980                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24981                     _this.gMapContext.locationName = results[0].formatted_address;
24982                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24983                     
24984                     _this.fireEvent('positionchanged', this, location);
24985                 }
24986             });
24987             
24988             return;
24989         }
24990         
24991         this.fireEvent('positionchanged', this, location);
24992     },
24993     
24994     resize: function()
24995     {
24996         google.maps.event.trigger(this.gMapContext.map, "resize");
24997         
24998         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24999         
25000         this.fireEvent('resize', this);
25001     },
25002     
25003     setPositionByLatLng: function(latitude, longitude)
25004     {
25005         this.setPosition(new google.maps.LatLng(latitude, longitude));
25006     },
25007     
25008     getCurrentPosition: function() 
25009     {
25010         return {
25011             latitude: this.gMapContext.location.lat(),
25012             longitude: this.gMapContext.location.lng()
25013         };
25014     },
25015     
25016     getAddressName: function() 
25017     {
25018         return this.gMapContext.locationName;
25019     },
25020     
25021     getAddressComponents: function() 
25022     {
25023         return this.gMapContext.addressComponents;
25024     },
25025     
25026     address_component_from_google_geocode: function(address_components) 
25027     {
25028         var result = {};
25029         
25030         for (var i = 0; i < address_components.length; i++) {
25031             var component = address_components[i];
25032             if (component.types.indexOf("postal_code") >= 0) {
25033                 result.postalCode = component.short_name;
25034             } else if (component.types.indexOf("street_number") >= 0) {
25035                 result.streetNumber = component.short_name;
25036             } else if (component.types.indexOf("route") >= 0) {
25037                 result.streetName = component.short_name;
25038             } else if (component.types.indexOf("neighborhood") >= 0) {
25039                 result.city = component.short_name;
25040             } else if (component.types.indexOf("locality") >= 0) {
25041                 result.city = component.short_name;
25042             } else if (component.types.indexOf("sublocality") >= 0) {
25043                 result.district = component.short_name;
25044             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25045                 result.stateOrProvince = component.short_name;
25046             } else if (component.types.indexOf("country") >= 0) {
25047                 result.country = component.short_name;
25048             }
25049         }
25050         
25051         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25052         result.addressLine2 = "";
25053         return result;
25054     },
25055     
25056     setZoomLevel: function(zoom)
25057     {
25058         this.gMapContext.map.setZoom(zoom);
25059     },
25060     
25061     show: function()
25062     {
25063         if(!this.el){
25064             return;
25065         }
25066         
25067         this.el.show();
25068         
25069         this.resize();
25070         
25071         this.fireEvent('show', this);
25072     },
25073     
25074     hide: function()
25075     {
25076         if(!this.el){
25077             return;
25078         }
25079         
25080         this.el.hide();
25081         
25082         this.fireEvent('hide', this);
25083     }
25084     
25085 });
25086
25087 Roo.apply(Roo.bootstrap.LocationPicker, {
25088     
25089     OverlayView : function(map, options)
25090     {
25091         options = options || {};
25092         
25093         this.setMap(map);
25094     }
25095     
25096     
25097 });/*
25098  * - LGPL
25099  *
25100  * Alert
25101  * 
25102  */
25103
25104 /**
25105  * @class Roo.bootstrap.Alert
25106  * @extends Roo.bootstrap.Component
25107  * Bootstrap Alert class
25108  * @cfg {String} title The title of alert
25109  * @cfg {String} html The content of alert
25110  * @cfg {String} weight (  success | info | warning | danger )
25111  * @cfg {String} faicon font-awesomeicon
25112  * 
25113  * @constructor
25114  * Create a new alert
25115  * @param {Object} config The config object
25116  */
25117
25118
25119 Roo.bootstrap.Alert = function(config){
25120     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25121     
25122 };
25123
25124 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25125     
25126     title: '',
25127     html: '',
25128     weight: false,
25129     faicon: false,
25130     
25131     getAutoCreate : function()
25132     {
25133         
25134         var cfg = {
25135             tag : 'div',
25136             cls : 'alert',
25137             cn : [
25138                 {
25139                     tag : 'i',
25140                     cls : 'roo-alert-icon'
25141                     
25142                 },
25143                 {
25144                     tag : 'b',
25145                     cls : 'roo-alert-title',
25146                     html : this.title
25147                 },
25148                 {
25149                     tag : 'span',
25150                     cls : 'roo-alert-text',
25151                     html : this.html
25152                 }
25153             ]
25154         };
25155         
25156         if(this.faicon){
25157             cfg.cn[0].cls += ' fa ' + this.faicon;
25158         }
25159         
25160         if(this.weight){
25161             cfg.cls += ' alert-' + this.weight;
25162         }
25163         
25164         return cfg;
25165     },
25166     
25167     initEvents: function() 
25168     {
25169         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25170     },
25171     
25172     setTitle : function(str)
25173     {
25174         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25175     },
25176     
25177     setText : function(str)
25178     {
25179         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25180     },
25181     
25182     setWeight : function(weight)
25183     {
25184         if(this.weight){
25185             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25186         }
25187         
25188         this.weight = weight;
25189         
25190         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25191     },
25192     
25193     setIcon : function(icon)
25194     {
25195         if(this.faicon){
25196             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25197         }
25198         
25199         this.faicon = icon;
25200         
25201         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25202     },
25203     
25204     hide: function() 
25205     {
25206         this.el.hide();   
25207     },
25208     
25209     show: function() 
25210     {  
25211         this.el.show();   
25212     }
25213     
25214 });
25215
25216  
25217 /*
25218 * Licence: LGPL
25219 */
25220
25221 /**
25222  * @class Roo.bootstrap.UploadCropbox
25223  * @extends Roo.bootstrap.Component
25224  * Bootstrap UploadCropbox class
25225  * @cfg {String} emptyText show when image has been loaded
25226  * @cfg {String} rotateNotify show when image too small to rotate
25227  * @cfg {Number} errorTimeout default 3000
25228  * @cfg {Number} minWidth default 300
25229  * @cfg {Number} minHeight default 300
25230  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25231  * @cfg {Boolean} isDocument (true|false) default false
25232  * @cfg {String} url action url
25233  * @cfg {String} paramName default 'imageUpload'
25234  * @cfg {String} method default POST
25235  * @cfg {Boolean} loadMask (true|false) default true
25236  * @cfg {Boolean} loadingText default 'Loading...'
25237  * 
25238  * @constructor
25239  * Create a new UploadCropbox
25240  * @param {Object} config The config object
25241  */
25242
25243 Roo.bootstrap.UploadCropbox = function(config){
25244     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25245     
25246     this.addEvents({
25247         /**
25248          * @event beforeselectfile
25249          * Fire before select file
25250          * @param {Roo.bootstrap.UploadCropbox} this
25251          */
25252         "beforeselectfile" : true,
25253         /**
25254          * @event initial
25255          * Fire after initEvent
25256          * @param {Roo.bootstrap.UploadCropbox} this
25257          */
25258         "initial" : true,
25259         /**
25260          * @event crop
25261          * Fire after initEvent
25262          * @param {Roo.bootstrap.UploadCropbox} this
25263          * @param {String} data
25264          */
25265         "crop" : true,
25266         /**
25267          * @event prepare
25268          * Fire when preparing the file data
25269          * @param {Roo.bootstrap.UploadCropbox} this
25270          * @param {Object} file
25271          */
25272         "prepare" : true,
25273         /**
25274          * @event exception
25275          * Fire when get exception
25276          * @param {Roo.bootstrap.UploadCropbox} this
25277          * @param {XMLHttpRequest} xhr
25278          */
25279         "exception" : true,
25280         /**
25281          * @event beforeloadcanvas
25282          * Fire before load the canvas
25283          * @param {Roo.bootstrap.UploadCropbox} this
25284          * @param {String} src
25285          */
25286         "beforeloadcanvas" : true,
25287         /**
25288          * @event trash
25289          * Fire when trash image
25290          * @param {Roo.bootstrap.UploadCropbox} this
25291          */
25292         "trash" : true,
25293         /**
25294          * @event download
25295          * Fire when download the image
25296          * @param {Roo.bootstrap.UploadCropbox} this
25297          */
25298         "download" : true,
25299         /**
25300          * @event footerbuttonclick
25301          * Fire when footerbuttonclick
25302          * @param {Roo.bootstrap.UploadCropbox} this
25303          * @param {String} type
25304          */
25305         "footerbuttonclick" : true,
25306         /**
25307          * @event resize
25308          * Fire when resize
25309          * @param {Roo.bootstrap.UploadCropbox} this
25310          */
25311         "resize" : true,
25312         /**
25313          * @event rotate
25314          * Fire when rotate the image
25315          * @param {Roo.bootstrap.UploadCropbox} this
25316          * @param {String} pos
25317          */
25318         "rotate" : true,
25319         /**
25320          * @event inspect
25321          * Fire when inspect the file
25322          * @param {Roo.bootstrap.UploadCropbox} this
25323          * @param {Object} file
25324          */
25325         "inspect" : true,
25326         /**
25327          * @event upload
25328          * Fire when xhr upload the file
25329          * @param {Roo.bootstrap.UploadCropbox} this
25330          * @param {Object} data
25331          */
25332         "upload" : true,
25333         /**
25334          * @event arrange
25335          * Fire when arrange the file data
25336          * @param {Roo.bootstrap.UploadCropbox} this
25337          * @param {Object} formData
25338          */
25339         "arrange" : true
25340     });
25341     
25342     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25343 };
25344
25345 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25346     
25347     emptyText : 'Click to upload image',
25348     rotateNotify : 'Image is too small to rotate',
25349     errorTimeout : 3000,
25350     scale : 0,
25351     baseScale : 1,
25352     rotate : 0,
25353     dragable : false,
25354     pinching : false,
25355     mouseX : 0,
25356     mouseY : 0,
25357     cropData : false,
25358     minWidth : 300,
25359     minHeight : 300,
25360     file : false,
25361     exif : {},
25362     baseRotate : 1,
25363     cropType : 'image/jpeg',
25364     buttons : false,
25365     canvasLoaded : false,
25366     isDocument : false,
25367     method : 'POST',
25368     paramName : 'imageUpload',
25369     loadMask : true,
25370     loadingText : 'Loading...',
25371     maskEl : false,
25372     
25373     getAutoCreate : function()
25374     {
25375         var cfg = {
25376             tag : 'div',
25377             cls : 'roo-upload-cropbox',
25378             cn : [
25379                 {
25380                     tag : 'input',
25381                     cls : 'roo-upload-cropbox-selector',
25382                     type : 'file'
25383                 },
25384                 {
25385                     tag : 'div',
25386                     cls : 'roo-upload-cropbox-body',
25387                     style : 'cursor:pointer',
25388                     cn : [
25389                         {
25390                             tag : 'div',
25391                             cls : 'roo-upload-cropbox-preview'
25392                         },
25393                         {
25394                             tag : 'div',
25395                             cls : 'roo-upload-cropbox-thumb'
25396                         },
25397                         {
25398                             tag : 'div',
25399                             cls : 'roo-upload-cropbox-empty-notify',
25400                             html : this.emptyText
25401                         },
25402                         {
25403                             tag : 'div',
25404                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25405                             html : this.rotateNotify
25406                         }
25407                     ]
25408                 },
25409                 {
25410                     tag : 'div',
25411                     cls : 'roo-upload-cropbox-footer',
25412                     cn : {
25413                         tag : 'div',
25414                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25415                         cn : []
25416                     }
25417                 }
25418             ]
25419         };
25420         
25421         return cfg;
25422     },
25423     
25424     onRender : function(ct, position)
25425     {
25426         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25427         
25428         if (this.buttons.length) {
25429             
25430             Roo.each(this.buttons, function(bb) {
25431                 
25432                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25433                 
25434                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25435                 
25436             }, this);
25437         }
25438         
25439         if(this.loadMask){
25440             this.maskEl = this.el;
25441         }
25442     },
25443     
25444     initEvents : function()
25445     {
25446         this.urlAPI = (window.createObjectURL && window) || 
25447                                 (window.URL && URL.revokeObjectURL && URL) || 
25448                                 (window.webkitURL && webkitURL);
25449                         
25450         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25451         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25452         
25453         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25454         this.selectorEl.hide();
25455         
25456         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25457         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25458         
25459         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25460         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25461         this.thumbEl.hide();
25462         
25463         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25464         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25465         
25466         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25467         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25468         this.errorEl.hide();
25469         
25470         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25471         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25472         this.footerEl.hide();
25473         
25474         this.setThumbBoxSize();
25475         
25476         this.bind();
25477         
25478         this.resize();
25479         
25480         this.fireEvent('initial', this);
25481     },
25482
25483     bind : function()
25484     {
25485         var _this = this;
25486         
25487         window.addEventListener("resize", function() { _this.resize(); } );
25488         
25489         this.bodyEl.on('click', this.beforeSelectFile, this);
25490         
25491         if(Roo.isTouch){
25492             this.bodyEl.on('touchstart', this.onTouchStart, this);
25493             this.bodyEl.on('touchmove', this.onTouchMove, this);
25494             this.bodyEl.on('touchend', this.onTouchEnd, this);
25495         }
25496         
25497         if(!Roo.isTouch){
25498             this.bodyEl.on('mousedown', this.onMouseDown, this);
25499             this.bodyEl.on('mousemove', this.onMouseMove, this);
25500             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25501             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25502             Roo.get(document).on('mouseup', this.onMouseUp, this);
25503         }
25504         
25505         this.selectorEl.on('change', this.onFileSelected, this);
25506     },
25507     
25508     reset : function()
25509     {    
25510         this.scale = 0;
25511         this.baseScale = 1;
25512         this.rotate = 0;
25513         this.baseRotate = 1;
25514         this.dragable = false;
25515         this.pinching = false;
25516         this.mouseX = 0;
25517         this.mouseY = 0;
25518         this.cropData = false;
25519         this.notifyEl.dom.innerHTML = this.emptyText;
25520         
25521         this.selectorEl.dom.value = '';
25522         
25523     },
25524     
25525     resize : function()
25526     {
25527         if(this.fireEvent('resize', this) != false){
25528             this.setThumbBoxPosition();
25529             this.setCanvasPosition();
25530         }
25531     },
25532     
25533     onFooterButtonClick : function(e, el, o, type)
25534     {
25535         switch (type) {
25536             case 'rotate-left' :
25537                 this.onRotateLeft(e);
25538                 break;
25539             case 'rotate-right' :
25540                 this.onRotateRight(e);
25541                 break;
25542             case 'picture' :
25543                 this.beforeSelectFile(e);
25544                 break;
25545             case 'trash' :
25546                 this.trash(e);
25547                 break;
25548             case 'crop' :
25549                 this.crop(e);
25550                 break;
25551             case 'download' :
25552                 this.download(e);
25553                 break;
25554             default :
25555                 break;
25556         }
25557         
25558         this.fireEvent('footerbuttonclick', this, type);
25559     },
25560     
25561     beforeSelectFile : function(e)
25562     {
25563         e.preventDefault();
25564         
25565         if(this.fireEvent('beforeselectfile', this) != false){
25566             this.selectorEl.dom.click();
25567         }
25568     },
25569     
25570     onFileSelected : function(e)
25571     {
25572         e.preventDefault();
25573         
25574         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25575             return;
25576         }
25577         
25578         var file = this.selectorEl.dom.files[0];
25579         
25580         if(this.fireEvent('inspect', this, file) != false){
25581             this.prepare(file);
25582         }
25583         
25584     },
25585     
25586     trash : function(e)
25587     {
25588         this.fireEvent('trash', this);
25589     },
25590     
25591     download : function(e)
25592     {
25593         this.fireEvent('download', this);
25594     },
25595     
25596     loadCanvas : function(src)
25597     {   
25598         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25599             
25600             this.reset();
25601             
25602             this.imageEl = document.createElement('img');
25603             
25604             var _this = this;
25605             
25606             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25607             
25608             this.imageEl.src = src;
25609         }
25610     },
25611     
25612     onLoadCanvas : function()
25613     {   
25614         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25615         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25616         
25617         this.bodyEl.un('click', this.beforeSelectFile, this);
25618         
25619         this.notifyEl.hide();
25620         this.thumbEl.show();
25621         this.footerEl.show();
25622         
25623         this.baseRotateLevel();
25624         
25625         if(this.isDocument){
25626             this.setThumbBoxSize();
25627         }
25628         
25629         this.setThumbBoxPosition();
25630         
25631         this.baseScaleLevel();
25632         
25633         this.draw();
25634         
25635         this.resize();
25636         
25637         this.canvasLoaded = true;
25638         
25639         if(this.loadMask){
25640             this.maskEl.unmask();
25641         }
25642         
25643     },
25644     
25645     setCanvasPosition : function()
25646     {   
25647         if(!this.canvasEl){
25648             return;
25649         }
25650         
25651         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25652         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25653         
25654         this.previewEl.setLeft(pw);
25655         this.previewEl.setTop(ph);
25656         
25657     },
25658     
25659     onMouseDown : function(e)
25660     {   
25661         e.stopEvent();
25662         
25663         this.dragable = true;
25664         this.pinching = false;
25665         
25666         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25667             this.dragable = false;
25668             return;
25669         }
25670         
25671         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25672         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25673         
25674     },
25675     
25676     onMouseMove : function(e)
25677     {   
25678         e.stopEvent();
25679         
25680         if(!this.canvasLoaded){
25681             return;
25682         }
25683         
25684         if (!this.dragable){
25685             return;
25686         }
25687         
25688         var minX = Math.ceil(this.thumbEl.getLeft(true));
25689         var minY = Math.ceil(this.thumbEl.getTop(true));
25690         
25691         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25692         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25693         
25694         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25695         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25696         
25697         x = x - this.mouseX;
25698         y = y - this.mouseY;
25699         
25700         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25701         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25702         
25703         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25704         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25705         
25706         this.previewEl.setLeft(bgX);
25707         this.previewEl.setTop(bgY);
25708         
25709         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25710         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25711     },
25712     
25713     onMouseUp : function(e)
25714     {   
25715         e.stopEvent();
25716         
25717         this.dragable = false;
25718     },
25719     
25720     onMouseWheel : function(e)
25721     {   
25722         e.stopEvent();
25723         
25724         this.startScale = this.scale;
25725         
25726         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25727         
25728         if(!this.zoomable()){
25729             this.scale = this.startScale;
25730             return;
25731         }
25732         
25733         this.draw();
25734         
25735         return;
25736     },
25737     
25738     zoomable : function()
25739     {
25740         var minScale = this.thumbEl.getWidth() / this.minWidth;
25741         
25742         if(this.minWidth < this.minHeight){
25743             minScale = this.thumbEl.getHeight() / this.minHeight;
25744         }
25745         
25746         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25747         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25748         
25749         if(
25750                 this.isDocument &&
25751                 (this.rotate == 0 || this.rotate == 180) && 
25752                 (
25753                     width > this.imageEl.OriginWidth || 
25754                     height > this.imageEl.OriginHeight ||
25755                     (width < this.minWidth && height < this.minHeight)
25756                 )
25757         ){
25758             return false;
25759         }
25760         
25761         if(
25762                 this.isDocument &&
25763                 (this.rotate == 90 || this.rotate == 270) && 
25764                 (
25765                     width > this.imageEl.OriginWidth || 
25766                     height > this.imageEl.OriginHeight ||
25767                     (width < this.minHeight && height < this.minWidth)
25768                 )
25769         ){
25770             return false;
25771         }
25772         
25773         if(
25774                 !this.isDocument &&
25775                 (this.rotate == 0 || this.rotate == 180) && 
25776                 (
25777                     width < this.minWidth || 
25778                     width > this.imageEl.OriginWidth || 
25779                     height < this.minHeight || 
25780                     height > this.imageEl.OriginHeight
25781                 )
25782         ){
25783             return false;
25784         }
25785         
25786         if(
25787                 !this.isDocument &&
25788                 (this.rotate == 90 || this.rotate == 270) && 
25789                 (
25790                     width < this.minHeight || 
25791                     width > this.imageEl.OriginWidth || 
25792                     height < this.minWidth || 
25793                     height > this.imageEl.OriginHeight
25794                 )
25795         ){
25796             return false;
25797         }
25798         
25799         return true;
25800         
25801     },
25802     
25803     onRotateLeft : function(e)
25804     {   
25805         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25806             
25807             var minScale = this.thumbEl.getWidth() / this.minWidth;
25808             
25809             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25810             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25811             
25812             this.startScale = this.scale;
25813             
25814             while (this.getScaleLevel() < minScale){
25815             
25816                 this.scale = this.scale + 1;
25817                 
25818                 if(!this.zoomable()){
25819                     break;
25820                 }
25821                 
25822                 if(
25823                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25824                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25825                 ){
25826                     continue;
25827                 }
25828                 
25829                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25830
25831                 this.draw();
25832                 
25833                 return;
25834             }
25835             
25836             this.scale = this.startScale;
25837             
25838             this.onRotateFail();
25839             
25840             return false;
25841         }
25842         
25843         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25844
25845         if(this.isDocument){
25846             this.setThumbBoxSize();
25847             this.setThumbBoxPosition();
25848             this.setCanvasPosition();
25849         }
25850         
25851         this.draw();
25852         
25853         this.fireEvent('rotate', this, 'left');
25854         
25855     },
25856     
25857     onRotateRight : function(e)
25858     {
25859         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25860             
25861             var minScale = this.thumbEl.getWidth() / this.minWidth;
25862         
25863             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25864             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25865             
25866             this.startScale = this.scale;
25867             
25868             while (this.getScaleLevel() < minScale){
25869             
25870                 this.scale = this.scale + 1;
25871                 
25872                 if(!this.zoomable()){
25873                     break;
25874                 }
25875                 
25876                 if(
25877                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25878                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25879                 ){
25880                     continue;
25881                 }
25882                 
25883                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25884
25885                 this.draw();
25886                 
25887                 return;
25888             }
25889             
25890             this.scale = this.startScale;
25891             
25892             this.onRotateFail();
25893             
25894             return false;
25895         }
25896         
25897         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25898
25899         if(this.isDocument){
25900             this.setThumbBoxSize();
25901             this.setThumbBoxPosition();
25902             this.setCanvasPosition();
25903         }
25904         
25905         this.draw();
25906         
25907         this.fireEvent('rotate', this, 'right');
25908     },
25909     
25910     onRotateFail : function()
25911     {
25912         this.errorEl.show(true);
25913         
25914         var _this = this;
25915         
25916         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25917     },
25918     
25919     draw : function()
25920     {
25921         this.previewEl.dom.innerHTML = '';
25922         
25923         var canvasEl = document.createElement("canvas");
25924         
25925         var contextEl = canvasEl.getContext("2d");
25926         
25927         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25928         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25929         var center = this.imageEl.OriginWidth / 2;
25930         
25931         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25932             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25933             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25934             center = this.imageEl.OriginHeight / 2;
25935         }
25936         
25937         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25938         
25939         contextEl.translate(center, center);
25940         contextEl.rotate(this.rotate * Math.PI / 180);
25941
25942         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25943         
25944         this.canvasEl = document.createElement("canvas");
25945         
25946         this.contextEl = this.canvasEl.getContext("2d");
25947         
25948         switch (this.rotate) {
25949             case 0 :
25950                 
25951                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25952                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25953                 
25954                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25955                 
25956                 break;
25957             case 90 : 
25958                 
25959                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25960                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25961                 
25962                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25963                     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);
25964                     break;
25965                 }
25966                 
25967                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25968                 
25969                 break;
25970             case 180 :
25971                 
25972                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25973                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25974                 
25975                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25976                     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);
25977                     break;
25978                 }
25979                 
25980                 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);
25981                 
25982                 break;
25983             case 270 :
25984                 
25985                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25986                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25987         
25988                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25989                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25990                     break;
25991                 }
25992                 
25993                 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);
25994                 
25995                 break;
25996             default : 
25997                 break;
25998         }
25999         
26000         this.previewEl.appendChild(this.canvasEl);
26001         
26002         this.setCanvasPosition();
26003     },
26004     
26005     crop : function()
26006     {
26007         if(!this.canvasLoaded){
26008             return;
26009         }
26010         
26011         var imageCanvas = document.createElement("canvas");
26012         
26013         var imageContext = imageCanvas.getContext("2d");
26014         
26015         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26016         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26017         
26018         var center = imageCanvas.width / 2;
26019         
26020         imageContext.translate(center, center);
26021         
26022         imageContext.rotate(this.rotate * Math.PI / 180);
26023         
26024         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26025         
26026         var canvas = document.createElement("canvas");
26027         
26028         var context = canvas.getContext("2d");
26029                 
26030         canvas.width = this.minWidth;
26031         canvas.height = this.minHeight;
26032
26033         switch (this.rotate) {
26034             case 0 :
26035                 
26036                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26037                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26038                 
26039                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26040                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26041                 
26042                 var targetWidth = this.minWidth - 2 * x;
26043                 var targetHeight = this.minHeight - 2 * y;
26044                 
26045                 var scale = 1;
26046                 
26047                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26048                     scale = targetWidth / width;
26049                 }
26050                 
26051                 if(x > 0 && y == 0){
26052                     scale = targetHeight / height;
26053                 }
26054                 
26055                 if(x > 0 && y > 0){
26056                     scale = targetWidth / width;
26057                     
26058                     if(width < height){
26059                         scale = targetHeight / height;
26060                     }
26061                 }
26062                 
26063                 context.scale(scale, scale);
26064                 
26065                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26066                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26067
26068                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26069                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26070
26071                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26072                 
26073                 break;
26074             case 90 : 
26075                 
26076                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26077                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26078                 
26079                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26080                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26081                 
26082                 var targetWidth = this.minWidth - 2 * x;
26083                 var targetHeight = this.minHeight - 2 * y;
26084                 
26085                 var scale = 1;
26086                 
26087                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26088                     scale = targetWidth / width;
26089                 }
26090                 
26091                 if(x > 0 && y == 0){
26092                     scale = targetHeight / height;
26093                 }
26094                 
26095                 if(x > 0 && y > 0){
26096                     scale = targetWidth / width;
26097                     
26098                     if(width < height){
26099                         scale = targetHeight / height;
26100                     }
26101                 }
26102                 
26103                 context.scale(scale, scale);
26104                 
26105                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26106                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26107
26108                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26109                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26110                 
26111                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26112                 
26113                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26114                 
26115                 break;
26116             case 180 :
26117                 
26118                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26119                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26120                 
26121                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26122                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26123                 
26124                 var targetWidth = this.minWidth - 2 * x;
26125                 var targetHeight = this.minHeight - 2 * y;
26126                 
26127                 var scale = 1;
26128                 
26129                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26130                     scale = targetWidth / width;
26131                 }
26132                 
26133                 if(x > 0 && y == 0){
26134                     scale = targetHeight / height;
26135                 }
26136                 
26137                 if(x > 0 && y > 0){
26138                     scale = targetWidth / width;
26139                     
26140                     if(width < height){
26141                         scale = targetHeight / height;
26142                     }
26143                 }
26144                 
26145                 context.scale(scale, scale);
26146                 
26147                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26148                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26149
26150                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26151                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26152
26153                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26154                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26155                 
26156                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26157                 
26158                 break;
26159             case 270 :
26160                 
26161                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26162                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26163                 
26164                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26165                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26166                 
26167                 var targetWidth = this.minWidth - 2 * x;
26168                 var targetHeight = this.minHeight - 2 * y;
26169                 
26170                 var scale = 1;
26171                 
26172                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26173                     scale = targetWidth / width;
26174                 }
26175                 
26176                 if(x > 0 && y == 0){
26177                     scale = targetHeight / height;
26178                 }
26179                 
26180                 if(x > 0 && y > 0){
26181                     scale = targetWidth / width;
26182                     
26183                     if(width < height){
26184                         scale = targetHeight / height;
26185                     }
26186                 }
26187                 
26188                 context.scale(scale, scale);
26189                 
26190                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26191                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26192
26193                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26194                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26195                 
26196                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26197                 
26198                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26199                 
26200                 break;
26201             default : 
26202                 break;
26203         }
26204         
26205         this.cropData = canvas.toDataURL(this.cropType);
26206         
26207         if(this.fireEvent('crop', this, this.cropData) !== false){
26208             this.process(this.file, this.cropData);
26209         }
26210         
26211         return;
26212         
26213     },
26214     
26215     setThumbBoxSize : function()
26216     {
26217         var width, height;
26218         
26219         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26220             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26221             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26222             
26223             this.minWidth = width;
26224             this.minHeight = height;
26225             
26226             if(this.rotate == 90 || this.rotate == 270){
26227                 this.minWidth = height;
26228                 this.minHeight = width;
26229             }
26230         }
26231         
26232         height = 300;
26233         width = Math.ceil(this.minWidth * height / this.minHeight);
26234         
26235         if(this.minWidth > this.minHeight){
26236             width = 300;
26237             height = Math.ceil(this.minHeight * width / this.minWidth);
26238         }
26239         
26240         this.thumbEl.setStyle({
26241             width : width + 'px',
26242             height : height + 'px'
26243         });
26244
26245         return;
26246             
26247     },
26248     
26249     setThumbBoxPosition : function()
26250     {
26251         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26252         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26253         
26254         this.thumbEl.setLeft(x);
26255         this.thumbEl.setTop(y);
26256         
26257     },
26258     
26259     baseRotateLevel : function()
26260     {
26261         this.baseRotate = 1;
26262         
26263         if(
26264                 typeof(this.exif) != 'undefined' &&
26265                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26266                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26267         ){
26268             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26269         }
26270         
26271         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26272         
26273     },
26274     
26275     baseScaleLevel : function()
26276     {
26277         var width, height;
26278         
26279         if(this.isDocument){
26280             
26281             if(this.baseRotate == 6 || this.baseRotate == 8){
26282             
26283                 height = this.thumbEl.getHeight();
26284                 this.baseScale = height / this.imageEl.OriginWidth;
26285
26286                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26287                     width = this.thumbEl.getWidth();
26288                     this.baseScale = width / this.imageEl.OriginHeight;
26289                 }
26290
26291                 return;
26292             }
26293
26294             height = this.thumbEl.getHeight();
26295             this.baseScale = height / this.imageEl.OriginHeight;
26296
26297             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26298                 width = this.thumbEl.getWidth();
26299                 this.baseScale = width / this.imageEl.OriginWidth;
26300             }
26301
26302             return;
26303         }
26304         
26305         if(this.baseRotate == 6 || this.baseRotate == 8){
26306             
26307             width = this.thumbEl.getHeight();
26308             this.baseScale = width / this.imageEl.OriginHeight;
26309             
26310             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26311                 height = this.thumbEl.getWidth();
26312                 this.baseScale = height / this.imageEl.OriginHeight;
26313             }
26314             
26315             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26316                 height = this.thumbEl.getWidth();
26317                 this.baseScale = height / this.imageEl.OriginHeight;
26318                 
26319                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26320                     width = this.thumbEl.getHeight();
26321                     this.baseScale = width / this.imageEl.OriginWidth;
26322                 }
26323             }
26324             
26325             return;
26326         }
26327         
26328         width = this.thumbEl.getWidth();
26329         this.baseScale = width / this.imageEl.OriginWidth;
26330         
26331         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26332             height = this.thumbEl.getHeight();
26333             this.baseScale = height / this.imageEl.OriginHeight;
26334         }
26335         
26336         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26337             
26338             height = this.thumbEl.getHeight();
26339             this.baseScale = height / this.imageEl.OriginHeight;
26340             
26341             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26342                 width = this.thumbEl.getWidth();
26343                 this.baseScale = width / this.imageEl.OriginWidth;
26344             }
26345             
26346         }
26347         
26348         return;
26349     },
26350     
26351     getScaleLevel : function()
26352     {
26353         return this.baseScale * Math.pow(1.1, this.scale);
26354     },
26355     
26356     onTouchStart : function(e)
26357     {
26358         if(!this.canvasLoaded){
26359             this.beforeSelectFile(e);
26360             return;
26361         }
26362         
26363         var touches = e.browserEvent.touches;
26364         
26365         if(!touches){
26366             return;
26367         }
26368         
26369         if(touches.length == 1){
26370             this.onMouseDown(e);
26371             return;
26372         }
26373         
26374         if(touches.length != 2){
26375             return;
26376         }
26377         
26378         var coords = [];
26379         
26380         for(var i = 0, finger; finger = touches[i]; i++){
26381             coords.push(finger.pageX, finger.pageY);
26382         }
26383         
26384         var x = Math.pow(coords[0] - coords[2], 2);
26385         var y = Math.pow(coords[1] - coords[3], 2);
26386         
26387         this.startDistance = Math.sqrt(x + y);
26388         
26389         this.startScale = this.scale;
26390         
26391         this.pinching = true;
26392         this.dragable = false;
26393         
26394     },
26395     
26396     onTouchMove : function(e)
26397     {
26398         if(!this.pinching && !this.dragable){
26399             return;
26400         }
26401         
26402         var touches = e.browserEvent.touches;
26403         
26404         if(!touches){
26405             return;
26406         }
26407         
26408         if(this.dragable){
26409             this.onMouseMove(e);
26410             return;
26411         }
26412         
26413         var coords = [];
26414         
26415         for(var i = 0, finger; finger = touches[i]; i++){
26416             coords.push(finger.pageX, finger.pageY);
26417         }
26418         
26419         var x = Math.pow(coords[0] - coords[2], 2);
26420         var y = Math.pow(coords[1] - coords[3], 2);
26421         
26422         this.endDistance = Math.sqrt(x + y);
26423         
26424         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26425         
26426         if(!this.zoomable()){
26427             this.scale = this.startScale;
26428             return;
26429         }
26430         
26431         this.draw();
26432         
26433     },
26434     
26435     onTouchEnd : function(e)
26436     {
26437         this.pinching = false;
26438         this.dragable = false;
26439         
26440     },
26441     
26442     process : function(file, crop)
26443     {
26444         if(this.loadMask){
26445             this.maskEl.mask(this.loadingText);
26446         }
26447         
26448         this.xhr = new XMLHttpRequest();
26449         
26450         file.xhr = this.xhr;
26451
26452         this.xhr.open(this.method, this.url, true);
26453         
26454         var headers = {
26455             "Accept": "application/json",
26456             "Cache-Control": "no-cache",
26457             "X-Requested-With": "XMLHttpRequest"
26458         };
26459         
26460         for (var headerName in headers) {
26461             var headerValue = headers[headerName];
26462             if (headerValue) {
26463                 this.xhr.setRequestHeader(headerName, headerValue);
26464             }
26465         }
26466         
26467         var _this = this;
26468         
26469         this.xhr.onload = function()
26470         {
26471             _this.xhrOnLoad(_this.xhr);
26472         }
26473         
26474         this.xhr.onerror = function()
26475         {
26476             _this.xhrOnError(_this.xhr);
26477         }
26478         
26479         var formData = new FormData();
26480
26481         formData.append('returnHTML', 'NO');
26482         
26483         if(crop){
26484             formData.append('crop', crop);
26485         }
26486         
26487         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26488             formData.append(this.paramName, file, file.name);
26489         }
26490         
26491         if(typeof(file.filename) != 'undefined'){
26492             formData.append('filename', file.filename);
26493         }
26494         
26495         if(typeof(file.mimetype) != 'undefined'){
26496             formData.append('mimetype', file.mimetype);
26497         }
26498         
26499         if(this.fireEvent('arrange', this, formData) != false){
26500             this.xhr.send(formData);
26501         };
26502     },
26503     
26504     xhrOnLoad : function(xhr)
26505     {
26506         if(this.loadMask){
26507             this.maskEl.unmask();
26508         }
26509         
26510         if (xhr.readyState !== 4) {
26511             this.fireEvent('exception', this, xhr);
26512             return;
26513         }
26514
26515         var response = Roo.decode(xhr.responseText);
26516         
26517         if(!response.success){
26518             this.fireEvent('exception', this, xhr);
26519             return;
26520         }
26521         
26522         var response = Roo.decode(xhr.responseText);
26523         
26524         this.fireEvent('upload', this, response);
26525         
26526     },
26527     
26528     xhrOnError : function()
26529     {
26530         if(this.loadMask){
26531             this.maskEl.unmask();
26532         }
26533         
26534         Roo.log('xhr on error');
26535         
26536         var response = Roo.decode(xhr.responseText);
26537           
26538         Roo.log(response);
26539         
26540     },
26541     
26542     prepare : function(file)
26543     {   
26544         if(this.loadMask){
26545             this.maskEl.mask(this.loadingText);
26546         }
26547         
26548         this.file = false;
26549         this.exif = {};
26550         
26551         if(typeof(file) === 'string'){
26552             this.loadCanvas(file);
26553             return;
26554         }
26555         
26556         if(!file || !this.urlAPI){
26557             return;
26558         }
26559         
26560         this.file = file;
26561         this.cropType = file.type;
26562         
26563         var _this = this;
26564         
26565         if(this.fireEvent('prepare', this, this.file) != false){
26566             
26567             var reader = new FileReader();
26568             
26569             reader.onload = function (e) {
26570                 if (e.target.error) {
26571                     Roo.log(e.target.error);
26572                     return;
26573                 }
26574                 
26575                 var buffer = e.target.result,
26576                     dataView = new DataView(buffer),
26577                     offset = 2,
26578                     maxOffset = dataView.byteLength - 4,
26579                     markerBytes,
26580                     markerLength;
26581                 
26582                 if (dataView.getUint16(0) === 0xffd8) {
26583                     while (offset < maxOffset) {
26584                         markerBytes = dataView.getUint16(offset);
26585                         
26586                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26587                             markerLength = dataView.getUint16(offset + 2) + 2;
26588                             if (offset + markerLength > dataView.byteLength) {
26589                                 Roo.log('Invalid meta data: Invalid segment size.');
26590                                 break;
26591                             }
26592                             
26593                             if(markerBytes == 0xffe1){
26594                                 _this.parseExifData(
26595                                     dataView,
26596                                     offset,
26597                                     markerLength
26598                                 );
26599                             }
26600                             
26601                             offset += markerLength;
26602                             
26603                             continue;
26604                         }
26605                         
26606                         break;
26607                     }
26608                     
26609                 }
26610                 
26611                 var url = _this.urlAPI.createObjectURL(_this.file);
26612                 
26613                 _this.loadCanvas(url);
26614                 
26615                 return;
26616             }
26617             
26618             reader.readAsArrayBuffer(this.file);
26619             
26620         }
26621         
26622     },
26623     
26624     parseExifData : function(dataView, offset, length)
26625     {
26626         var tiffOffset = offset + 10,
26627             littleEndian,
26628             dirOffset;
26629     
26630         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26631             // No Exif data, might be XMP data instead
26632             return;
26633         }
26634         
26635         // Check for the ASCII code for "Exif" (0x45786966):
26636         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26637             // No Exif data, might be XMP data instead
26638             return;
26639         }
26640         if (tiffOffset + 8 > dataView.byteLength) {
26641             Roo.log('Invalid Exif data: Invalid segment size.');
26642             return;
26643         }
26644         // Check for the two null bytes:
26645         if (dataView.getUint16(offset + 8) !== 0x0000) {
26646             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26647             return;
26648         }
26649         // Check the byte alignment:
26650         switch (dataView.getUint16(tiffOffset)) {
26651         case 0x4949:
26652             littleEndian = true;
26653             break;
26654         case 0x4D4D:
26655             littleEndian = false;
26656             break;
26657         default:
26658             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26659             return;
26660         }
26661         // Check for the TIFF tag marker (0x002A):
26662         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26663             Roo.log('Invalid Exif data: Missing TIFF marker.');
26664             return;
26665         }
26666         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26667         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26668         
26669         this.parseExifTags(
26670             dataView,
26671             tiffOffset,
26672             tiffOffset + dirOffset,
26673             littleEndian
26674         );
26675     },
26676     
26677     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26678     {
26679         var tagsNumber,
26680             dirEndOffset,
26681             i;
26682         if (dirOffset + 6 > dataView.byteLength) {
26683             Roo.log('Invalid Exif data: Invalid directory offset.');
26684             return;
26685         }
26686         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26687         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26688         if (dirEndOffset + 4 > dataView.byteLength) {
26689             Roo.log('Invalid Exif data: Invalid directory size.');
26690             return;
26691         }
26692         for (i = 0; i < tagsNumber; i += 1) {
26693             this.parseExifTag(
26694                 dataView,
26695                 tiffOffset,
26696                 dirOffset + 2 + 12 * i, // tag offset
26697                 littleEndian
26698             );
26699         }
26700         // Return the offset to the next directory:
26701         return dataView.getUint32(dirEndOffset, littleEndian);
26702     },
26703     
26704     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26705     {
26706         var tag = dataView.getUint16(offset, littleEndian);
26707         
26708         this.exif[tag] = this.getExifValue(
26709             dataView,
26710             tiffOffset,
26711             offset,
26712             dataView.getUint16(offset + 2, littleEndian), // tag type
26713             dataView.getUint32(offset + 4, littleEndian), // tag length
26714             littleEndian
26715         );
26716     },
26717     
26718     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26719     {
26720         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26721             tagSize,
26722             dataOffset,
26723             values,
26724             i,
26725             str,
26726             c;
26727     
26728         if (!tagType) {
26729             Roo.log('Invalid Exif data: Invalid tag type.');
26730             return;
26731         }
26732         
26733         tagSize = tagType.size * length;
26734         // Determine if the value is contained in the dataOffset bytes,
26735         // or if the value at the dataOffset is a pointer to the actual data:
26736         dataOffset = tagSize > 4 ?
26737                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26738         if (dataOffset + tagSize > dataView.byteLength) {
26739             Roo.log('Invalid Exif data: Invalid data offset.');
26740             return;
26741         }
26742         if (length === 1) {
26743             return tagType.getValue(dataView, dataOffset, littleEndian);
26744         }
26745         values = [];
26746         for (i = 0; i < length; i += 1) {
26747             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26748         }
26749         
26750         if (tagType.ascii) {
26751             str = '';
26752             // Concatenate the chars:
26753             for (i = 0; i < values.length; i += 1) {
26754                 c = values[i];
26755                 // Ignore the terminating NULL byte(s):
26756                 if (c === '\u0000') {
26757                     break;
26758                 }
26759                 str += c;
26760             }
26761             return str;
26762         }
26763         return values;
26764     }
26765     
26766 });
26767
26768 Roo.apply(Roo.bootstrap.UploadCropbox, {
26769     tags : {
26770         'Orientation': 0x0112
26771     },
26772     
26773     Orientation: {
26774             1: 0, //'top-left',
26775 //            2: 'top-right',
26776             3: 180, //'bottom-right',
26777 //            4: 'bottom-left',
26778 //            5: 'left-top',
26779             6: 90, //'right-top',
26780 //            7: 'right-bottom',
26781             8: 270 //'left-bottom'
26782     },
26783     
26784     exifTagTypes : {
26785         // byte, 8-bit unsigned int:
26786         1: {
26787             getValue: function (dataView, dataOffset) {
26788                 return dataView.getUint8(dataOffset);
26789             },
26790             size: 1
26791         },
26792         // ascii, 8-bit byte:
26793         2: {
26794             getValue: function (dataView, dataOffset) {
26795                 return String.fromCharCode(dataView.getUint8(dataOffset));
26796             },
26797             size: 1,
26798             ascii: true
26799         },
26800         // short, 16 bit int:
26801         3: {
26802             getValue: function (dataView, dataOffset, littleEndian) {
26803                 return dataView.getUint16(dataOffset, littleEndian);
26804             },
26805             size: 2
26806         },
26807         // long, 32 bit int:
26808         4: {
26809             getValue: function (dataView, dataOffset, littleEndian) {
26810                 return dataView.getUint32(dataOffset, littleEndian);
26811             },
26812             size: 4
26813         },
26814         // rational = two long values, first is numerator, second is denominator:
26815         5: {
26816             getValue: function (dataView, dataOffset, littleEndian) {
26817                 return dataView.getUint32(dataOffset, littleEndian) /
26818                     dataView.getUint32(dataOffset + 4, littleEndian);
26819             },
26820             size: 8
26821         },
26822         // slong, 32 bit signed int:
26823         9: {
26824             getValue: function (dataView, dataOffset, littleEndian) {
26825                 return dataView.getInt32(dataOffset, littleEndian);
26826             },
26827             size: 4
26828         },
26829         // srational, two slongs, first is numerator, second is denominator:
26830         10: {
26831             getValue: function (dataView, dataOffset, littleEndian) {
26832                 return dataView.getInt32(dataOffset, littleEndian) /
26833                     dataView.getInt32(dataOffset + 4, littleEndian);
26834             },
26835             size: 8
26836         }
26837     },
26838     
26839     footer : {
26840         STANDARD : [
26841             {
26842                 tag : 'div',
26843                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26844                 action : 'rotate-left',
26845                 cn : [
26846                     {
26847                         tag : 'button',
26848                         cls : 'btn btn-default',
26849                         html : '<i class="fa fa-undo"></i>'
26850                     }
26851                 ]
26852             },
26853             {
26854                 tag : 'div',
26855                 cls : 'btn-group roo-upload-cropbox-picture',
26856                 action : 'picture',
26857                 cn : [
26858                     {
26859                         tag : 'button',
26860                         cls : 'btn btn-default',
26861                         html : '<i class="fa fa-picture-o"></i>'
26862                     }
26863                 ]
26864             },
26865             {
26866                 tag : 'div',
26867                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26868                 action : 'rotate-right',
26869                 cn : [
26870                     {
26871                         tag : 'button',
26872                         cls : 'btn btn-default',
26873                         html : '<i class="fa fa-repeat"></i>'
26874                     }
26875                 ]
26876             }
26877         ],
26878         DOCUMENT : [
26879             {
26880                 tag : 'div',
26881                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26882                 action : 'rotate-left',
26883                 cn : [
26884                     {
26885                         tag : 'button',
26886                         cls : 'btn btn-default',
26887                         html : '<i class="fa fa-undo"></i>'
26888                     }
26889                 ]
26890             },
26891             {
26892                 tag : 'div',
26893                 cls : 'btn-group roo-upload-cropbox-download',
26894                 action : 'download',
26895                 cn : [
26896                     {
26897                         tag : 'button',
26898                         cls : 'btn btn-default',
26899                         html : '<i class="fa fa-download"></i>'
26900                     }
26901                 ]
26902             },
26903             {
26904                 tag : 'div',
26905                 cls : 'btn-group roo-upload-cropbox-crop',
26906                 action : 'crop',
26907                 cn : [
26908                     {
26909                         tag : 'button',
26910                         cls : 'btn btn-default',
26911                         html : '<i class="fa fa-crop"></i>'
26912                     }
26913                 ]
26914             },
26915             {
26916                 tag : 'div',
26917                 cls : 'btn-group roo-upload-cropbox-trash',
26918                 action : 'trash',
26919                 cn : [
26920                     {
26921                         tag : 'button',
26922                         cls : 'btn btn-default',
26923                         html : '<i class="fa fa-trash"></i>'
26924                     }
26925                 ]
26926             },
26927             {
26928                 tag : 'div',
26929                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26930                 action : 'rotate-right',
26931                 cn : [
26932                     {
26933                         tag : 'button',
26934                         cls : 'btn btn-default',
26935                         html : '<i class="fa fa-repeat"></i>'
26936                     }
26937                 ]
26938             }
26939         ],
26940         ROTATOR : [
26941             {
26942                 tag : 'div',
26943                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26944                 action : 'rotate-left',
26945                 cn : [
26946                     {
26947                         tag : 'button',
26948                         cls : 'btn btn-default',
26949                         html : '<i class="fa fa-undo"></i>'
26950                     }
26951                 ]
26952             },
26953             {
26954                 tag : 'div',
26955                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26956                 action : 'rotate-right',
26957                 cn : [
26958                     {
26959                         tag : 'button',
26960                         cls : 'btn btn-default',
26961                         html : '<i class="fa fa-repeat"></i>'
26962                     }
26963                 ]
26964             }
26965         ]
26966     }
26967 });
26968
26969 /*
26970 * Licence: LGPL
26971 */
26972
26973 /**
26974  * @class Roo.bootstrap.DocumentManager
26975  * @extends Roo.bootstrap.Component
26976  * Bootstrap DocumentManager class
26977  * @cfg {String} paramName default 'imageUpload'
26978  * @cfg {String} method default POST
26979  * @cfg {String} url action url
26980  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26981  * @cfg {Boolean} multiple multiple upload default true
26982  * @cfg {Number} thumbSize default 300
26983  * @cfg {String} fieldLabel
26984  * @cfg {Number} labelWidth default 4
26985  * @cfg {String} labelAlign (left|top) default left
26986  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26987  * 
26988  * @constructor
26989  * Create a new DocumentManager
26990  * @param {Object} config The config object
26991  */
26992
26993 Roo.bootstrap.DocumentManager = function(config){
26994     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26995     
26996     this.addEvents({
26997         /**
26998          * @event initial
26999          * Fire when initial the DocumentManager
27000          * @param {Roo.bootstrap.DocumentManager} this
27001          */
27002         "initial" : true,
27003         /**
27004          * @event inspect
27005          * inspect selected file
27006          * @param {Roo.bootstrap.DocumentManager} this
27007          * @param {File} file
27008          */
27009         "inspect" : true,
27010         /**
27011          * @event exception
27012          * Fire when xhr load exception
27013          * @param {Roo.bootstrap.DocumentManager} this
27014          * @param {XMLHttpRequest} xhr
27015          */
27016         "exception" : true,
27017         /**
27018          * @event prepare
27019          * prepare the form data
27020          * @param {Roo.bootstrap.DocumentManager} this
27021          * @param {Object} formData
27022          */
27023         "prepare" : true,
27024         /**
27025          * @event remove
27026          * Fire when remove the file
27027          * @param {Roo.bootstrap.DocumentManager} this
27028          * @param {Object} file
27029          */
27030         "remove" : true,
27031         /**
27032          * @event refresh
27033          * Fire after refresh the file
27034          * @param {Roo.bootstrap.DocumentManager} this
27035          */
27036         "refresh" : true,
27037         /**
27038          * @event click
27039          * Fire after click the image
27040          * @param {Roo.bootstrap.DocumentManager} this
27041          * @param {Object} file
27042          */
27043         "click" : true,
27044         /**
27045          * @event edit
27046          * Fire when upload a image and editable set to true
27047          * @param {Roo.bootstrap.DocumentManager} this
27048          * @param {Object} file
27049          */
27050         "edit" : true,
27051         /**
27052          * @event beforeselectfile
27053          * Fire before select file
27054          * @param {Roo.bootstrap.DocumentManager} this
27055          */
27056         "beforeselectfile" : true,
27057         /**
27058          * @event process
27059          * Fire before process file
27060          * @param {Roo.bootstrap.DocumentManager} this
27061          * @param {Object} file
27062          */
27063         "process" : true
27064         
27065     });
27066 };
27067
27068 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27069     
27070     boxes : 0,
27071     inputName : '',
27072     thumbSize : 300,
27073     multiple : true,
27074     files : [],
27075     method : 'POST',
27076     url : '',
27077     paramName : 'imageUpload',
27078     fieldLabel : '',
27079     labelWidth : 4,
27080     labelAlign : 'left',
27081     editable : true,
27082     delegates : [],
27083     
27084     
27085     xhr : false, 
27086     
27087     getAutoCreate : function()
27088     {   
27089         var managerWidget = {
27090             tag : 'div',
27091             cls : 'roo-document-manager',
27092             cn : [
27093                 {
27094                     tag : 'input',
27095                     cls : 'roo-document-manager-selector',
27096                     type : 'file'
27097                 },
27098                 {
27099                     tag : 'div',
27100                     cls : 'roo-document-manager-uploader',
27101                     cn : [
27102                         {
27103                             tag : 'div',
27104                             cls : 'roo-document-manager-upload-btn',
27105                             html : '<i class="fa fa-plus"></i>'
27106                         }
27107                     ]
27108                     
27109                 }
27110             ]
27111         };
27112         
27113         var content = [
27114             {
27115                 tag : 'div',
27116                 cls : 'column col-md-12',
27117                 cn : managerWidget
27118             }
27119         ];
27120         
27121         if(this.fieldLabel.length){
27122             
27123             content = [
27124                 {
27125                     tag : 'div',
27126                     cls : 'column col-md-12',
27127                     html : this.fieldLabel
27128                 },
27129                 {
27130                     tag : 'div',
27131                     cls : 'column col-md-12',
27132                     cn : managerWidget
27133                 }
27134             ];
27135
27136             if(this.labelAlign == 'left'){
27137                 content = [
27138                     {
27139                         tag : 'div',
27140                         cls : 'column col-md-' + this.labelWidth,
27141                         html : this.fieldLabel
27142                     },
27143                     {
27144                         tag : 'div',
27145                         cls : 'column col-md-' + (12 - this.labelWidth),
27146                         cn : managerWidget
27147                     }
27148                 ];
27149                 
27150             }
27151         }
27152         
27153         var cfg = {
27154             tag : 'div',
27155             cls : 'row clearfix',
27156             cn : content
27157         };
27158         
27159         return cfg;
27160         
27161     },
27162     
27163     initEvents : function()
27164     {
27165         this.managerEl = this.el.select('.roo-document-manager', true).first();
27166         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27167         
27168         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27169         this.selectorEl.hide();
27170         
27171         if(this.multiple){
27172             this.selectorEl.attr('multiple', 'multiple');
27173         }
27174         
27175         this.selectorEl.on('change', this.onFileSelected, this);
27176         
27177         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27178         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27179         
27180         this.uploader.on('click', this.onUploaderClick, this);
27181         
27182         this.renderProgressDialog();
27183         
27184         var _this = this;
27185         
27186         window.addEventListener("resize", function() { _this.refresh(); } );
27187         
27188         this.fireEvent('initial', this);
27189     },
27190     
27191     renderProgressDialog : function()
27192     {
27193         var _this = this;
27194         
27195         this.progressDialog = new Roo.bootstrap.Modal({
27196             cls : 'roo-document-manager-progress-dialog',
27197             allow_close : false,
27198             title : '',
27199             buttons : [
27200                 {
27201                     name  :'cancel',
27202                     weight : 'danger',
27203                     html : 'Cancel'
27204                 }
27205             ], 
27206             listeners : { 
27207                 btnclick : function() {
27208                     _this.uploadCancel();
27209                     this.hide();
27210                 }
27211             }
27212         });
27213          
27214         this.progressDialog.render(Roo.get(document.body));
27215          
27216         this.progress = new Roo.bootstrap.Progress({
27217             cls : 'roo-document-manager-progress',
27218             active : true,
27219             striped : true
27220         });
27221         
27222         this.progress.render(this.progressDialog.getChildContainer());
27223         
27224         this.progressBar = new Roo.bootstrap.ProgressBar({
27225             cls : 'roo-document-manager-progress-bar',
27226             aria_valuenow : 0,
27227             aria_valuemin : 0,
27228             aria_valuemax : 12,
27229             panel : 'success'
27230         });
27231         
27232         this.progressBar.render(this.progress.getChildContainer());
27233     },
27234     
27235     onUploaderClick : function(e)
27236     {
27237         e.preventDefault();
27238      
27239         if(this.fireEvent('beforeselectfile', this) != false){
27240             this.selectorEl.dom.click();
27241         }
27242         
27243     },
27244     
27245     onFileSelected : function(e)
27246     {
27247         e.preventDefault();
27248         
27249         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27250             return;
27251         }
27252         
27253         Roo.each(this.selectorEl.dom.files, function(file){
27254             if(this.fireEvent('inspect', this, file) != false){
27255                 this.files.push(file);
27256             }
27257         }, this);
27258         
27259         this.queue();
27260         
27261     },
27262     
27263     queue : function()
27264     {
27265         this.selectorEl.dom.value = '';
27266         
27267         if(!this.files.length){
27268             return;
27269         }
27270         
27271         if(this.boxes > 0 && this.files.length > this.boxes){
27272             this.files = this.files.slice(0, this.boxes);
27273         }
27274         
27275         this.uploader.show();
27276         
27277         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27278             this.uploader.hide();
27279         }
27280         
27281         var _this = this;
27282         
27283         var files = [];
27284         
27285         var docs = [];
27286         
27287         Roo.each(this.files, function(file){
27288             
27289             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27290                 var f = this.renderPreview(file);
27291                 files.push(f);
27292                 return;
27293             }
27294             
27295             if(file.type.indexOf('image') != -1){
27296                 this.delegates.push(
27297                     (function(){
27298                         _this.process(file);
27299                     }).createDelegate(this)
27300                 );
27301         
27302                 return;
27303             }
27304             
27305             docs.push(
27306                 (function(){
27307                     _this.process(file);
27308                 }).createDelegate(this)
27309             );
27310             
27311         }, this);
27312         
27313         this.files = files;
27314         
27315         this.delegates = this.delegates.concat(docs);
27316         
27317         if(!this.delegates.length){
27318             this.refresh();
27319             return;
27320         }
27321         
27322         this.progressBar.aria_valuemax = this.delegates.length;
27323         
27324         this.arrange();
27325         
27326         return;
27327     },
27328     
27329     arrange : function()
27330     {
27331         if(!this.delegates.length){
27332             this.progressDialog.hide();
27333             this.refresh();
27334             return;
27335         }
27336         
27337         var delegate = this.delegates.shift();
27338         
27339         this.progressDialog.show();
27340         
27341         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27342         
27343         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27344         
27345         delegate();
27346     },
27347     
27348     refresh : function()
27349     {
27350         this.uploader.show();
27351         
27352         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27353             this.uploader.hide();
27354         }
27355         
27356         Roo.isTouch ? this.closable(false) : this.closable(true);
27357         
27358         this.fireEvent('refresh', this);
27359     },
27360     
27361     onRemove : function(e, el, o)
27362     {
27363         e.preventDefault();
27364         
27365         this.fireEvent('remove', this, o);
27366         
27367     },
27368     
27369     remove : function(o)
27370     {
27371         var files = [];
27372         
27373         Roo.each(this.files, function(file){
27374             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27375                 files.push(file);
27376                 return;
27377             }
27378
27379             o.target.remove();
27380
27381         }, this);
27382         
27383         this.files = files;
27384         
27385         this.refresh();
27386     },
27387     
27388     clear : function()
27389     {
27390         Roo.each(this.files, function(file){
27391             if(!file.target){
27392                 return;
27393             }
27394             
27395             file.target.remove();
27396
27397         }, this);
27398         
27399         this.files = [];
27400         
27401         this.refresh();
27402     },
27403     
27404     onClick : function(e, el, o)
27405     {
27406         e.preventDefault();
27407         
27408         this.fireEvent('click', this, o);
27409         
27410     },
27411     
27412     closable : function(closable)
27413     {
27414         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27415             
27416             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27417             
27418             if(closable){
27419                 el.show();
27420                 return;
27421             }
27422             
27423             el.hide();
27424             
27425         }, this);
27426     },
27427     
27428     xhrOnLoad : function(xhr)
27429     {
27430         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27431             el.remove();
27432         }, this);
27433         
27434         if (xhr.readyState !== 4) {
27435             this.arrange();
27436             this.fireEvent('exception', this, xhr);
27437             return;
27438         }
27439
27440         var response = Roo.decode(xhr.responseText);
27441         
27442         if(!response.success){
27443             this.arrange();
27444             this.fireEvent('exception', this, xhr);
27445             return;
27446         }
27447         
27448         var file = this.renderPreview(response.data);
27449         
27450         this.files.push(file);
27451         
27452         this.arrange();
27453         
27454     },
27455     
27456     xhrOnError : function(xhr)
27457     {
27458         Roo.log('xhr on error');
27459         
27460         var response = Roo.decode(xhr.responseText);
27461           
27462         Roo.log(response);
27463         
27464         this.arrange();
27465     },
27466     
27467     process : function(file)
27468     {
27469         if(this.fireEvent('process', this, file) !== false){
27470             if(this.editable && file.type.indexOf('image') != -1){
27471                 this.fireEvent('edit', this, file);
27472                 return;
27473             }
27474
27475             this.uploadStart(file, false);
27476
27477             return;
27478         }
27479         
27480     },
27481     
27482     uploadStart : function(file, crop)
27483     {
27484         this.xhr = new XMLHttpRequest();
27485         
27486         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27487             this.arrange();
27488             return;
27489         }
27490         
27491         file.xhr = this.xhr;
27492             
27493         this.managerEl.createChild({
27494             tag : 'div',
27495             cls : 'roo-document-manager-loading',
27496             cn : [
27497                 {
27498                     tag : 'div',
27499                     tooltip : file.name,
27500                     cls : 'roo-document-manager-thumb',
27501                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27502                 }
27503             ]
27504
27505         });
27506
27507         this.xhr.open(this.method, this.url, true);
27508         
27509         var headers = {
27510             "Accept": "application/json",
27511             "Cache-Control": "no-cache",
27512             "X-Requested-With": "XMLHttpRequest"
27513         };
27514         
27515         for (var headerName in headers) {
27516             var headerValue = headers[headerName];
27517             if (headerValue) {
27518                 this.xhr.setRequestHeader(headerName, headerValue);
27519             }
27520         }
27521         
27522         var _this = this;
27523         
27524         this.xhr.onload = function()
27525         {
27526             _this.xhrOnLoad(_this.xhr);
27527         }
27528         
27529         this.xhr.onerror = function()
27530         {
27531             _this.xhrOnError(_this.xhr);
27532         }
27533         
27534         var formData = new FormData();
27535
27536         formData.append('returnHTML', 'NO');
27537         
27538         if(crop){
27539             formData.append('crop', crop);
27540         }
27541         
27542         formData.append(this.paramName, file, file.name);
27543         
27544         if(this.fireEvent('prepare', this, formData) != false){
27545             this.xhr.send(formData);
27546         };
27547     },
27548     
27549     uploadCancel : function()
27550     {
27551         if (this.xhr) {
27552             this.xhr.abort();
27553         }
27554         
27555         
27556         this.delegates = [];
27557         
27558         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27559             el.remove();
27560         }, this);
27561         
27562         this.arrange();
27563     },
27564     
27565     renderPreview : function(file)
27566     {
27567         if(typeof(file.target) != 'undefined' && file.target){
27568             return file;
27569         }
27570         
27571         var previewEl = this.managerEl.createChild({
27572             tag : 'div',
27573             cls : 'roo-document-manager-preview',
27574             cn : [
27575                 {
27576                     tag : 'div',
27577                     tooltip : file.filename,
27578                     cls : 'roo-document-manager-thumb',
27579                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27580                 },
27581                 {
27582                     tag : 'button',
27583                     cls : 'close',
27584                     html : '<i class="fa fa-times-circle"></i>'
27585                 }
27586             ]
27587         });
27588
27589         var close = previewEl.select('button.close', true).first();
27590
27591         close.on('click', this.onRemove, this, file);
27592
27593         file.target = previewEl;
27594
27595         var image = previewEl.select('img', true).first();
27596         
27597         var _this = this;
27598         
27599         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27600         
27601         image.on('click', this.onClick, this, file);
27602         
27603         return file;
27604         
27605     },
27606     
27607     onPreviewLoad : function(file, image)
27608     {
27609         if(typeof(file.target) == 'undefined' || !file.target){
27610             return;
27611         }
27612         
27613         var width = image.dom.naturalWidth || image.dom.width;
27614         var height = image.dom.naturalHeight || image.dom.height;
27615         
27616         if(width > height){
27617             file.target.addClass('wide');
27618             return;
27619         }
27620         
27621         file.target.addClass('tall');
27622         return;
27623         
27624     },
27625     
27626     uploadFromSource : function(file, crop)
27627     {
27628         this.xhr = new XMLHttpRequest();
27629         
27630         this.managerEl.createChild({
27631             tag : 'div',
27632             cls : 'roo-document-manager-loading',
27633             cn : [
27634                 {
27635                     tag : 'div',
27636                     tooltip : file.name,
27637                     cls : 'roo-document-manager-thumb',
27638                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27639                 }
27640             ]
27641
27642         });
27643
27644         this.xhr.open(this.method, this.url, true);
27645         
27646         var headers = {
27647             "Accept": "application/json",
27648             "Cache-Control": "no-cache",
27649             "X-Requested-With": "XMLHttpRequest"
27650         };
27651         
27652         for (var headerName in headers) {
27653             var headerValue = headers[headerName];
27654             if (headerValue) {
27655                 this.xhr.setRequestHeader(headerName, headerValue);
27656             }
27657         }
27658         
27659         var _this = this;
27660         
27661         this.xhr.onload = function()
27662         {
27663             _this.xhrOnLoad(_this.xhr);
27664         }
27665         
27666         this.xhr.onerror = function()
27667         {
27668             _this.xhrOnError(_this.xhr);
27669         }
27670         
27671         var formData = new FormData();
27672
27673         formData.append('returnHTML', 'NO');
27674         
27675         formData.append('crop', crop);
27676         
27677         if(typeof(file.filename) != 'undefined'){
27678             formData.append('filename', file.filename);
27679         }
27680         
27681         if(typeof(file.mimetype) != 'undefined'){
27682             formData.append('mimetype', file.mimetype);
27683         }
27684         
27685         if(this.fireEvent('prepare', this, formData) != false){
27686             this.xhr.send(formData);
27687         };
27688     }
27689 });
27690
27691 /*
27692 * Licence: LGPL
27693 */
27694
27695 /**
27696  * @class Roo.bootstrap.DocumentViewer
27697  * @extends Roo.bootstrap.Component
27698  * Bootstrap DocumentViewer class
27699  * 
27700  * @constructor
27701  * Create a new DocumentViewer
27702  * @param {Object} config The config object
27703  */
27704
27705 Roo.bootstrap.DocumentViewer = function(config){
27706     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27707     
27708     this.addEvents({
27709         /**
27710          * @event initial
27711          * Fire after initEvent
27712          * @param {Roo.bootstrap.DocumentViewer} this
27713          */
27714         "initial" : true,
27715         /**
27716          * @event click
27717          * Fire after click
27718          * @param {Roo.bootstrap.DocumentViewer} this
27719          */
27720         "click" : true,
27721         /**
27722          * @event trash
27723          * Fire after trash button
27724          * @param {Roo.bootstrap.DocumentViewer} this
27725          */
27726         "trash" : true
27727         
27728     });
27729 };
27730
27731 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27732     
27733     getAutoCreate : function()
27734     {
27735         var cfg = {
27736             tag : 'div',
27737             cls : 'roo-document-viewer',
27738             cn : [
27739                 {
27740                     tag : 'div',
27741                     cls : 'roo-document-viewer-body',
27742                     cn : [
27743                         {
27744                             tag : 'div',
27745                             cls : 'roo-document-viewer-thumb',
27746                             cn : [
27747                                 {
27748                                     tag : 'img',
27749                                     cls : 'roo-document-viewer-image'
27750                                 }
27751                             ]
27752                         }
27753                     ]
27754                 },
27755                 {
27756                     tag : 'div',
27757                     cls : 'roo-document-viewer-footer',
27758                     cn : {
27759                         tag : 'div',
27760                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27761                         cn : [
27762                             {
27763                                 tag : 'div',
27764                                 cls : 'btn-group',
27765                                 cn : [
27766                                     {
27767                                         tag : 'button',
27768                                         cls : 'btn btn-default roo-document-viewer-trash',
27769                                         html : '<i class="fa fa-trash"></i>'
27770                                     }
27771                                 ]
27772                             }
27773                         ]
27774                     }
27775                 }
27776             ]
27777         };
27778         
27779         return cfg;
27780     },
27781     
27782     initEvents : function()
27783     {
27784         
27785         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27786         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27787         
27788         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27789         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27790         
27791         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27792         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27793         
27794         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27795         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27796         
27797         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27798         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27799         
27800         this.bodyEl.on('click', this.onClick, this);
27801         
27802         this.trashBtn.on('click', this.onTrash, this);
27803         
27804     },
27805     
27806     initial : function()
27807     {
27808 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27809         
27810         
27811         this.fireEvent('initial', this);
27812         
27813     },
27814     
27815     onClick : function(e)
27816     {
27817         e.preventDefault();
27818         
27819         this.fireEvent('click', this);
27820     },
27821     
27822     onTrash : function(e)
27823     {
27824         e.preventDefault();
27825         
27826         this.fireEvent('trash', this);
27827     }
27828     
27829 });
27830 /*
27831  * - LGPL
27832  *
27833  * nav progress bar
27834  * 
27835  */
27836
27837 /**
27838  * @class Roo.bootstrap.NavProgressBar
27839  * @extends Roo.bootstrap.Component
27840  * Bootstrap NavProgressBar class
27841  * 
27842  * @constructor
27843  * Create a new nav progress bar
27844  * @param {Object} config The config object
27845  */
27846
27847 Roo.bootstrap.NavProgressBar = function(config){
27848     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27849
27850     this.bullets = this.bullets || [];
27851    
27852 //    Roo.bootstrap.NavProgressBar.register(this);
27853      this.addEvents({
27854         /**
27855              * @event changed
27856              * Fires when the active item changes
27857              * @param {Roo.bootstrap.NavProgressBar} this
27858              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27859              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27860          */
27861         'changed': true
27862      });
27863     
27864 };
27865
27866 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27867     
27868     bullets : [],
27869     barItems : [],
27870     
27871     getAutoCreate : function()
27872     {
27873         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27874         
27875         cfg = {
27876             tag : 'div',
27877             cls : 'roo-navigation-bar-group',
27878             cn : [
27879                 {
27880                     tag : 'div',
27881                     cls : 'roo-navigation-top-bar'
27882                 },
27883                 {
27884                     tag : 'div',
27885                     cls : 'roo-navigation-bullets-bar',
27886                     cn : [
27887                         {
27888                             tag : 'ul',
27889                             cls : 'roo-navigation-bar'
27890                         }
27891                     ]
27892                 },
27893                 
27894                 {
27895                     tag : 'div',
27896                     cls : 'roo-navigation-bottom-bar'
27897                 }
27898             ]
27899             
27900         };
27901         
27902         return cfg;
27903         
27904     },
27905     
27906     initEvents: function() 
27907     {
27908         
27909     },
27910     
27911     onRender : function(ct, position) 
27912     {
27913         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27914         
27915         if(this.bullets.length){
27916             Roo.each(this.bullets, function(b){
27917                this.addItem(b);
27918             }, this);
27919         }
27920         
27921         this.format();
27922         
27923     },
27924     
27925     addItem : function(cfg)
27926     {
27927         var item = new Roo.bootstrap.NavProgressItem(cfg);
27928         
27929         item.parentId = this.id;
27930         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27931         
27932         if(cfg.html){
27933             var top = new Roo.bootstrap.Element({
27934                 tag : 'div',
27935                 cls : 'roo-navigation-bar-text'
27936             });
27937             
27938             var bottom = new Roo.bootstrap.Element({
27939                 tag : 'div',
27940                 cls : 'roo-navigation-bar-text'
27941             });
27942             
27943             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27944             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27945             
27946             var topText = new Roo.bootstrap.Element({
27947                 tag : 'span',
27948                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27949             });
27950             
27951             var bottomText = new Roo.bootstrap.Element({
27952                 tag : 'span',
27953                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27954             });
27955             
27956             topText.onRender(top.el, null);
27957             bottomText.onRender(bottom.el, null);
27958             
27959             item.topEl = top;
27960             item.bottomEl = bottom;
27961         }
27962         
27963         this.barItems.push(item);
27964         
27965         return item;
27966     },
27967     
27968     getActive : function()
27969     {
27970         var active = false;
27971         
27972         Roo.each(this.barItems, function(v){
27973             
27974             if (!v.isActive()) {
27975                 return;
27976             }
27977             
27978             active = v;
27979             return false;
27980             
27981         });
27982         
27983         return active;
27984     },
27985     
27986     setActiveItem : function(item)
27987     {
27988         var prev = false;
27989         
27990         Roo.each(this.barItems, function(v){
27991             if (v.rid == item.rid) {
27992                 return ;
27993             }
27994             
27995             if (v.isActive()) {
27996                 v.setActive(false);
27997                 prev = v;
27998             }
27999         });
28000
28001         item.setActive(true);
28002         
28003         this.fireEvent('changed', this, item, prev);
28004     },
28005     
28006     getBarItem: function(rid)
28007     {
28008         var ret = false;
28009         
28010         Roo.each(this.barItems, function(e) {
28011             if (e.rid != rid) {
28012                 return;
28013             }
28014             
28015             ret =  e;
28016             return false;
28017         });
28018         
28019         return ret;
28020     },
28021     
28022     indexOfItem : function(item)
28023     {
28024         var index = false;
28025         
28026         Roo.each(this.barItems, function(v, i){
28027             
28028             if (v.rid != item.rid) {
28029                 return;
28030             }
28031             
28032             index = i;
28033             return false
28034         });
28035         
28036         return index;
28037     },
28038     
28039     setActiveNext : function()
28040     {
28041         var i = this.indexOfItem(this.getActive());
28042         
28043         if (i > this.barItems.length) {
28044             return;
28045         }
28046         
28047         this.setActiveItem(this.barItems[i+1]);
28048     },
28049     
28050     setActivePrev : function()
28051     {
28052         var i = this.indexOfItem(this.getActive());
28053         
28054         if (i  < 1) {
28055             return;
28056         }
28057         
28058         this.setActiveItem(this.barItems[i-1]);
28059     },
28060     
28061     format : function()
28062     {
28063         if(!this.barItems.length){
28064             return;
28065         }
28066      
28067         var width = 100 / this.barItems.length;
28068         
28069         Roo.each(this.barItems, function(i){
28070             i.el.setStyle('width', width + '%');
28071             i.topEl.el.setStyle('width', width + '%');
28072             i.bottomEl.el.setStyle('width', width + '%');
28073         }, this);
28074         
28075     }
28076     
28077 });
28078 /*
28079  * - LGPL
28080  *
28081  * Nav Progress Item
28082  * 
28083  */
28084
28085 /**
28086  * @class Roo.bootstrap.NavProgressItem
28087  * @extends Roo.bootstrap.Component
28088  * Bootstrap NavProgressItem class
28089  * @cfg {String} rid the reference id
28090  * @cfg {Boolean} active (true|false) Is item active default false
28091  * @cfg {Boolean} disabled (true|false) Is item active default false
28092  * @cfg {String} html
28093  * @cfg {String} position (top|bottom) text position default bottom
28094  * @cfg {String} icon show icon instead of number
28095  * 
28096  * @constructor
28097  * Create a new NavProgressItem
28098  * @param {Object} config The config object
28099  */
28100 Roo.bootstrap.NavProgressItem = function(config){
28101     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28102     this.addEvents({
28103         // raw events
28104         /**
28105          * @event click
28106          * The raw click event for the entire grid.
28107          * @param {Roo.bootstrap.NavProgressItem} this
28108          * @param {Roo.EventObject} e
28109          */
28110         "click" : true
28111     });
28112    
28113 };
28114
28115 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28116     
28117     rid : '',
28118     active : false,
28119     disabled : false,
28120     html : '',
28121     position : 'bottom',
28122     icon : false,
28123     
28124     getAutoCreate : function()
28125     {
28126         var iconCls = 'roo-navigation-bar-item-icon';
28127         
28128         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28129         
28130         var cfg = {
28131             tag: 'li',
28132             cls: 'roo-navigation-bar-item',
28133             cn : [
28134                 {
28135                     tag : 'i',
28136                     cls : iconCls
28137                 }
28138             ]
28139         };
28140         
28141         if(this.active){
28142             cfg.cls += ' active';
28143         }
28144         if(this.disabled){
28145             cfg.cls += ' disabled';
28146         }
28147         
28148         return cfg;
28149     },
28150     
28151     disable : function()
28152     {
28153         this.setDisabled(true);
28154     },
28155     
28156     enable : function()
28157     {
28158         this.setDisabled(false);
28159     },
28160     
28161     initEvents: function() 
28162     {
28163         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28164         
28165         this.iconEl.on('click', this.onClick, this);
28166     },
28167     
28168     onClick : function(e)
28169     {
28170         e.preventDefault();
28171         
28172         if(this.disabled){
28173             return;
28174         }
28175         
28176         if(this.fireEvent('click', this, e) === false){
28177             return;
28178         };
28179         
28180         this.parent().setActiveItem(this);
28181     },
28182     
28183     isActive: function () 
28184     {
28185         return this.active;
28186     },
28187     
28188     setActive : function(state)
28189     {
28190         if(this.active == state){
28191             return;
28192         }
28193         
28194         this.active = state;
28195         
28196         if (state) {
28197             this.el.addClass('active');
28198             return;
28199         }
28200         
28201         this.el.removeClass('active');
28202         
28203         return;
28204     },
28205     
28206     setDisabled : function(state)
28207     {
28208         if(this.disabled == state){
28209             return;
28210         }
28211         
28212         this.disabled = state;
28213         
28214         if (state) {
28215             this.el.addClass('disabled');
28216             return;
28217         }
28218         
28219         this.el.removeClass('disabled');
28220     },
28221     
28222     tooltipEl : function()
28223     {
28224         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28225     }
28226 });
28227  
28228
28229  /*
28230  * - LGPL
28231  *
28232  * FieldLabel
28233  * 
28234  */
28235
28236 /**
28237  * @class Roo.bootstrap.FieldLabel
28238  * @extends Roo.bootstrap.Component
28239  * Bootstrap FieldLabel class
28240  * @cfg {String} html contents of the element
28241  * @cfg {String} tag tag of the element default label
28242  * @cfg {String} cls class of the element
28243  * @cfg {String} target label target 
28244  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28245  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28246  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28247  * @cfg {String} iconTooltip default "This field is required"
28248  * 
28249  * @constructor
28250  * Create a new FieldLabel
28251  * @param {Object} config The config object
28252  */
28253
28254 Roo.bootstrap.FieldLabel = function(config){
28255     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28256     
28257     this.addEvents({
28258             /**
28259              * @event invalid
28260              * Fires after the field has been marked as invalid.
28261              * @param {Roo.form.FieldLabel} this
28262              * @param {String} msg The validation message
28263              */
28264             invalid : true,
28265             /**
28266              * @event valid
28267              * Fires after the field has been validated with no errors.
28268              * @param {Roo.form.FieldLabel} this
28269              */
28270             valid : true
28271         });
28272 };
28273
28274 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28275     
28276     tag: 'label',
28277     cls: '',
28278     html: '',
28279     target: '',
28280     allowBlank : true,
28281     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28282     validClass : 'text-success fa fa-lg fa-check',
28283     iconTooltip : 'This field is required',
28284     
28285     getAutoCreate : function(){
28286         
28287         var cfg = {
28288             tag : this.tag,
28289             cls : 'roo-bootstrap-field-label ' + this.cls,
28290             for : this.target,
28291             cn : [
28292                 {
28293                     tag : 'i',
28294                     cls : '',
28295                     tooltip : this.iconTooltip
28296                 },
28297                 {
28298                     tag : 'span',
28299                     html : this.html
28300                 }
28301             ] 
28302         };
28303         
28304         return cfg;
28305     },
28306     
28307     initEvents: function() 
28308     {
28309         Roo.bootstrap.Element.superclass.initEvents.call(this);
28310         
28311         this.iconEl = this.el.select('i', true).first();
28312         
28313         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28314         
28315         Roo.bootstrap.FieldLabel.register(this);
28316     },
28317     
28318     /**
28319      * Mark this field as valid
28320      */
28321     markValid : function()
28322     {
28323         this.iconEl.show();
28324         
28325         this.iconEl.removeClass(this.invalidClass);
28326         
28327         this.iconEl.addClass(this.validClass);
28328         
28329         this.fireEvent('valid', this);
28330     },
28331     
28332     /**
28333      * Mark this field as invalid
28334      * @param {String} msg The validation message
28335      */
28336     markInvalid : function(msg)
28337     {
28338         this.iconEl.show();
28339         
28340         this.iconEl.removeClass(this.validClass);
28341         
28342         this.iconEl.addClass(this.invalidClass);
28343         
28344         this.fireEvent('invalid', this, msg);
28345     }
28346     
28347    
28348 });
28349
28350 Roo.apply(Roo.bootstrap.FieldLabel, {
28351     
28352     groups: {},
28353     
28354      /**
28355     * register a FieldLabel Group
28356     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28357     */
28358     register : function(label)
28359     {
28360         if(this.groups.hasOwnProperty(label.target)){
28361             return;
28362         }
28363      
28364         this.groups[label.target] = label;
28365         
28366     },
28367     /**
28368     * fetch a FieldLabel Group based on the target
28369     * @param {string} target
28370     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28371     */
28372     get: function(target) {
28373         if (typeof(this.groups[target]) == 'undefined') {
28374             return false;
28375         }
28376         
28377         return this.groups[target] ;
28378     }
28379 });
28380
28381  
28382
28383  /*
28384  * - LGPL
28385  *
28386  * page DateSplitField.
28387  * 
28388  */
28389
28390
28391 /**
28392  * @class Roo.bootstrap.DateSplitField
28393  * @extends Roo.bootstrap.Component
28394  * Bootstrap DateSplitField class
28395  * @cfg {string} fieldLabel - the label associated
28396  * @cfg {Number} labelWidth set the width of label (0-12)
28397  * @cfg {String} labelAlign (top|left)
28398  * @cfg {Boolean} dayAllowBlank (true|false) default false
28399  * @cfg {Boolean} monthAllowBlank (true|false) default false
28400  * @cfg {Boolean} yearAllowBlank (true|false) default false
28401  * @cfg {string} dayPlaceholder 
28402  * @cfg {string} monthPlaceholder
28403  * @cfg {string} yearPlaceholder
28404  * @cfg {string} dayFormat default 'd'
28405  * @cfg {string} monthFormat default 'm'
28406  * @cfg {string} yearFormat default 'Y'
28407
28408  *     
28409  * @constructor
28410  * Create a new DateSplitField
28411  * @param {Object} config The config object
28412  */
28413
28414 Roo.bootstrap.DateSplitField = function(config){
28415     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28416     
28417     this.addEvents({
28418         // raw events
28419          /**
28420          * @event years
28421          * getting the data of years
28422          * @param {Roo.bootstrap.DateSplitField} this
28423          * @param {Object} years
28424          */
28425         "years" : true,
28426         /**
28427          * @event days
28428          * getting the data of days
28429          * @param {Roo.bootstrap.DateSplitField} this
28430          * @param {Object} days
28431          */
28432         "days" : true,
28433         /**
28434          * @event invalid
28435          * Fires after the field has been marked as invalid.
28436          * @param {Roo.form.Field} this
28437          * @param {String} msg The validation message
28438          */
28439         invalid : true,
28440        /**
28441          * @event valid
28442          * Fires after the field has been validated with no errors.
28443          * @param {Roo.form.Field} this
28444          */
28445         valid : true
28446     });
28447 };
28448
28449 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28450     
28451     fieldLabel : '',
28452     labelAlign : 'top',
28453     labelWidth : 3,
28454     dayAllowBlank : false,
28455     monthAllowBlank : false,
28456     yearAllowBlank : false,
28457     dayPlaceholder : '',
28458     monthPlaceholder : '',
28459     yearPlaceholder : '',
28460     dayFormat : 'd',
28461     monthFormat : 'm',
28462     yearFormat : 'Y',
28463     isFormField : true,
28464     
28465     getAutoCreate : function()
28466     {
28467         var cfg = {
28468             tag : 'div',
28469             cls : 'row roo-date-split-field-group',
28470             cn : [
28471                 {
28472                     tag : 'input',
28473                     type : 'hidden',
28474                     cls : 'form-hidden-field roo-date-split-field-group-value',
28475                     name : this.name
28476                 }
28477             ]
28478         };
28479         
28480         if(this.fieldLabel){
28481             cfg.cn.push({
28482                 tag : 'div',
28483                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28484                 cn : [
28485                     {
28486                         tag : 'label',
28487                         html : this.fieldLabel
28488                     }
28489                 ]
28490             });
28491         }
28492         
28493         Roo.each(['day', 'month', 'year'], function(t){
28494             cfg.cn.push({
28495                 tag : 'div',
28496                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28497             });
28498         }, this);
28499         
28500         return cfg;
28501     },
28502     
28503     inputEl: function ()
28504     {
28505         return this.el.select('.roo-date-split-field-group-value', true).first();
28506     },
28507     
28508     onRender : function(ct, position) 
28509     {
28510         var _this = this;
28511         
28512         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28513         
28514         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28515         
28516         this.dayField = new Roo.bootstrap.ComboBox({
28517             allowBlank : this.dayAllowBlank,
28518             alwaysQuery : true,
28519             displayField : 'value',
28520             editable : false,
28521             fieldLabel : '',
28522             forceSelection : true,
28523             mode : 'local',
28524             placeholder : this.dayPlaceholder,
28525             selectOnFocus : true,
28526             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28527             triggerAction : 'all',
28528             typeAhead : true,
28529             valueField : 'value',
28530             store : new Roo.data.SimpleStore({
28531                 data : (function() {    
28532                     var days = [];
28533                     _this.fireEvent('days', _this, days);
28534                     return days;
28535                 })(),
28536                 fields : [ 'value' ]
28537             }),
28538             listeners : {
28539                 select : function (_self, record, index)
28540                 {
28541                     _this.setValue(_this.getValue());
28542                 }
28543             }
28544         });
28545
28546         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28547         
28548         this.monthField = new Roo.bootstrap.MonthField({
28549             after : '<i class=\"fa fa-calendar\"></i>',
28550             allowBlank : this.monthAllowBlank,
28551             placeholder : this.monthPlaceholder,
28552             readOnly : true,
28553             listeners : {
28554                 render : function (_self)
28555                 {
28556                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28557                         e.preventDefault();
28558                         _self.focus();
28559                     });
28560                 },
28561                 select : function (_self, oldvalue, newvalue)
28562                 {
28563                     _this.setValue(_this.getValue());
28564                 }
28565             }
28566         });
28567         
28568         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28569         
28570         this.yearField = new Roo.bootstrap.ComboBox({
28571             allowBlank : this.yearAllowBlank,
28572             alwaysQuery : true,
28573             displayField : 'value',
28574             editable : false,
28575             fieldLabel : '',
28576             forceSelection : true,
28577             mode : 'local',
28578             placeholder : this.yearPlaceholder,
28579             selectOnFocus : true,
28580             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28581             triggerAction : 'all',
28582             typeAhead : true,
28583             valueField : 'value',
28584             store : new Roo.data.SimpleStore({
28585                 data : (function() {
28586                     var years = [];
28587                     _this.fireEvent('years', _this, years);
28588                     return years;
28589                 })(),
28590                 fields : [ 'value' ]
28591             }),
28592             listeners : {
28593                 select : function (_self, record, index)
28594                 {
28595                     _this.setValue(_this.getValue());
28596                 }
28597             }
28598         });
28599
28600         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28601     },
28602     
28603     setValue : function(v, format)
28604     {
28605         this.inputEl.dom.value = v;
28606         
28607         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28608         
28609         var d = Date.parseDate(v, f);
28610         
28611         if(!d){
28612             this.validate();
28613             return;
28614         }
28615         
28616         this.setDay(d.format(this.dayFormat));
28617         this.setMonth(d.format(this.monthFormat));
28618         this.setYear(d.format(this.yearFormat));
28619         
28620         this.validate();
28621         
28622         return;
28623     },
28624     
28625     setDay : function(v)
28626     {
28627         this.dayField.setValue(v);
28628         this.inputEl.dom.value = this.getValue();
28629         this.validate();
28630         return;
28631     },
28632     
28633     setMonth : function(v)
28634     {
28635         this.monthField.setValue(v, true);
28636         this.inputEl.dom.value = this.getValue();
28637         this.validate();
28638         return;
28639     },
28640     
28641     setYear : function(v)
28642     {
28643         this.yearField.setValue(v);
28644         this.inputEl.dom.value = this.getValue();
28645         this.validate();
28646         return;
28647     },
28648     
28649     getDay : function()
28650     {
28651         return this.dayField.getValue();
28652     },
28653     
28654     getMonth : function()
28655     {
28656         return this.monthField.getValue();
28657     },
28658     
28659     getYear : function()
28660     {
28661         return this.yearField.getValue();
28662     },
28663     
28664     getValue : function()
28665     {
28666         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28667         
28668         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28669         
28670         return date;
28671     },
28672     
28673     reset : function()
28674     {
28675         this.setDay('');
28676         this.setMonth('');
28677         this.setYear('');
28678         this.inputEl.dom.value = '';
28679         this.validate();
28680         return;
28681     },
28682     
28683     validate : function()
28684     {
28685         var d = this.dayField.validate();
28686         var m = this.monthField.validate();
28687         var y = this.yearField.validate();
28688         
28689         var valid = true;
28690         
28691         if(
28692                 (!this.dayAllowBlank && !d) ||
28693                 (!this.monthAllowBlank && !m) ||
28694                 (!this.yearAllowBlank && !y)
28695         ){
28696             valid = false;
28697         }
28698         
28699         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28700             return valid;
28701         }
28702         
28703         if(valid){
28704             this.markValid();
28705             return valid;
28706         }
28707         
28708         this.markInvalid();
28709         
28710         return valid;
28711     },
28712     
28713     markValid : function()
28714     {
28715         
28716         var label = this.el.select('label', true).first();
28717         var icon = this.el.select('i.fa-star', true).first();
28718
28719         if(label && icon){
28720             icon.remove();
28721         }
28722         
28723         this.fireEvent('valid', this);
28724     },
28725     
28726      /**
28727      * Mark this field as invalid
28728      * @param {String} msg The validation message
28729      */
28730     markInvalid : function(msg)
28731     {
28732         
28733         var label = this.el.select('label', true).first();
28734         var icon = this.el.select('i.fa-star', true).first();
28735
28736         if(label && !icon){
28737             this.el.select('.roo-date-split-field-label', true).createChild({
28738                 tag : 'i',
28739                 cls : 'text-danger fa fa-lg fa-star',
28740                 tooltip : 'This field is required',
28741                 style : 'margin-right:5px;'
28742             }, label, true);
28743         }
28744         
28745         this.fireEvent('invalid', this, msg);
28746     },
28747     
28748     clearInvalid : function()
28749     {
28750         var label = this.el.select('label', true).first();
28751         var icon = this.el.select('i.fa-star', true).first();
28752
28753         if(label && icon){
28754             icon.remove();
28755         }
28756         
28757         this.fireEvent('valid', this);
28758     },
28759     
28760     getName: function()
28761     {
28762         return this.name;
28763     }
28764     
28765 });
28766
28767  /**
28768  *
28769  * This is based on 
28770  * http://masonry.desandro.com
28771  *
28772  * The idea is to render all the bricks based on vertical width...
28773  *
28774  * The original code extends 'outlayer' - we might need to use that....
28775  * 
28776  */
28777
28778
28779 /**
28780  * @class Roo.bootstrap.LayoutMasonry
28781  * @extends Roo.bootstrap.Component
28782  * Bootstrap Layout Masonry class
28783  * 
28784  * @constructor
28785  * Create a new Element
28786  * @param {Object} config The config object
28787  */
28788
28789 Roo.bootstrap.LayoutMasonry = function(config){
28790     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28791     
28792     this.bricks = [];
28793     
28794 };
28795
28796 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28797     
28798     /**
28799      * @cfg {Boolean} isLayoutInstant = no animation?
28800      */   
28801     isLayoutInstant : false, // needed?
28802    
28803     /**
28804      * @cfg {Number} boxWidth  width of the columns
28805      */   
28806     boxWidth : 450,
28807     
28808       /**
28809      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28810      */   
28811     boxHeight : 0,
28812     
28813     /**
28814      * @cfg {Number} padWidth padding below box..
28815      */   
28816     padWidth : 10, 
28817     
28818     /**
28819      * @cfg {Number} gutter gutter width..
28820      */   
28821     gutter : 10,
28822     
28823      /**
28824      * @cfg {Number} maxCols maximum number of columns
28825      */   
28826     
28827     maxCols: 0,
28828     
28829     /**
28830      * @cfg {Boolean} isAutoInitial defalut true
28831      */   
28832     isAutoInitial : true, 
28833     
28834     containerWidth: 0,
28835     
28836     /**
28837      * @cfg {Boolean} isHorizontal defalut false
28838      */   
28839     isHorizontal : false, 
28840
28841     currentSize : null,
28842     
28843     tag: 'div',
28844     
28845     cls: '',
28846     
28847     bricks: null, //CompositeElement
28848     
28849     cols : 1,
28850     
28851     _isLayoutInited : false,
28852     
28853 //    isAlternative : false, // only use for vertical layout...
28854     
28855     /**
28856      * @cfg {Number} alternativePadWidth padding below box..
28857      */   
28858     alternativePadWidth : 50, 
28859     
28860     getAutoCreate : function(){
28861         
28862         var cfg = {
28863             tag: this.tag,
28864             cls: 'blog-masonary-wrapper ' + this.cls,
28865             cn : {
28866                 cls : 'mas-boxes masonary'
28867             }
28868         };
28869         
28870         return cfg;
28871     },
28872     
28873     getChildContainer: function( )
28874     {
28875         if (this.boxesEl) {
28876             return this.boxesEl;
28877         }
28878         
28879         this.boxesEl = this.el.select('.mas-boxes').first();
28880         
28881         return this.boxesEl;
28882     },
28883     
28884     
28885     initEvents : function()
28886     {
28887         var _this = this;
28888         
28889         if(this.isAutoInitial){
28890             Roo.log('hook children rendered');
28891             this.on('childrenrendered', function() {
28892                 Roo.log('children rendered');
28893                 _this.initial();
28894             } ,this);
28895         }
28896     },
28897     
28898     initial : function()
28899     {
28900         this.currentSize = this.el.getBox(true);
28901         
28902         Roo.EventManager.onWindowResize(this.resize, this); 
28903
28904         if(!this.isAutoInitial){
28905             this.layout();
28906             return;
28907         }
28908         
28909         this.layout();
28910         
28911         return;
28912         //this.layout.defer(500,this);
28913         
28914     },
28915     
28916     resize : function()
28917     {
28918         Roo.log('resize');
28919         
28920         var cs = this.el.getBox(true);
28921         
28922         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28923             Roo.log("no change in with or X");
28924             return;
28925         }
28926         
28927         this.currentSize = cs;
28928         
28929         this.layout();
28930         
28931     },
28932     
28933     layout : function()
28934     {   
28935         this._resetLayout();
28936         
28937         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28938         
28939         this.layoutItems( isInstant );
28940       
28941         this._isLayoutInited = true;
28942         
28943     },
28944     
28945     _resetLayout : function()
28946     {
28947         if(this.isHorizontal){
28948             this.horizontalMeasureColumns();
28949             return;
28950         }
28951         
28952         this.verticalMeasureColumns();
28953         
28954     },
28955     
28956     verticalMeasureColumns : function()
28957     {
28958         this.getContainerWidth();
28959         
28960 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28961 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28962 //            return;
28963 //        }
28964         
28965         var boxWidth = this.boxWidth + this.padWidth;
28966         
28967         if(this.containerWidth < this.boxWidth){
28968             boxWidth = this.containerWidth
28969         }
28970         
28971         var containerWidth = this.containerWidth;
28972         
28973         var cols = Math.floor(containerWidth / boxWidth);
28974         
28975         this.cols = Math.max( cols, 1 );
28976         
28977         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28978         
28979         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28980         
28981         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28982         
28983         this.colWidth = boxWidth + avail - this.padWidth;
28984         
28985         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28986         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28987     },
28988     
28989     horizontalMeasureColumns : function()
28990     {
28991         this.getContainerWidth();
28992         
28993         var boxWidth = this.boxWidth;
28994         
28995         if(this.containerWidth < boxWidth){
28996             boxWidth = this.containerWidth;
28997         }
28998         
28999         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29000         
29001         this.el.setHeight(boxWidth);
29002         
29003     },
29004     
29005     getContainerWidth : function()
29006     {
29007         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29008     },
29009     
29010     layoutItems : function( isInstant )
29011     {
29012         var items = Roo.apply([], this.bricks);
29013         
29014         if(this.isHorizontal){
29015             this._horizontalLayoutItems( items , isInstant );
29016             return;
29017         }
29018         
29019 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29020 //            this._verticalAlternativeLayoutItems( items , isInstant );
29021 //            return;
29022 //        }
29023         
29024         this._verticalLayoutItems( items , isInstant );
29025         
29026     },
29027     
29028     _verticalLayoutItems : function ( items , isInstant)
29029     {
29030         if ( !items || !items.length ) {
29031             return;
29032         }
29033         
29034         var standard = [
29035             ['xs', 'xs', 'xs', 'tall'],
29036             ['xs', 'xs', 'tall'],
29037             ['xs', 'xs', 'sm'],
29038             ['xs', 'xs', 'xs'],
29039             ['xs', 'tall'],
29040             ['xs', 'sm'],
29041             ['xs', 'xs'],
29042             ['xs'],
29043             
29044             ['sm', 'xs', 'xs'],
29045             ['sm', 'xs'],
29046             ['sm'],
29047             
29048             ['tall', 'xs', 'xs', 'xs'],
29049             ['tall', 'xs', 'xs'],
29050             ['tall', 'xs'],
29051             ['tall']
29052             
29053         ];
29054         
29055         var queue = [];
29056         
29057         var boxes = [];
29058         
29059         var box = [];
29060         
29061         Roo.each(items, function(item, k){
29062             
29063             switch (item.size) {
29064                 // these layouts take up a full box,
29065                 case 'md' :
29066                 case 'md-left' :
29067                 case 'md-right' :
29068                 case 'wide' :
29069                     
29070                     if(box.length){
29071                         boxes.push(box);
29072                         box = [];
29073                     }
29074                     
29075                     boxes.push([item]);
29076                     
29077                     break;
29078                     
29079                 case 'xs' :
29080                 case 'sm' :
29081                 case 'tall' :
29082                     
29083                     box.push(item);
29084                     
29085                     break;
29086                 default :
29087                     break;
29088                     
29089             }
29090             
29091         }, this);
29092         
29093         if(box.length){
29094             boxes.push(box);
29095             box = [];
29096         }
29097         
29098         var filterPattern = function(box, length)
29099         {
29100             if(!box.length){
29101                 return;
29102             }
29103             
29104             var match = false;
29105             
29106             var pattern = box.slice(0, length);
29107             
29108             var format = [];
29109             
29110             Roo.each(pattern, function(i){
29111                 format.push(i.size);
29112             }, this);
29113             
29114             Roo.each(standard, function(s){
29115                 
29116                 if(String(s) != String(format)){
29117                     return;
29118                 }
29119                 
29120                 match = true;
29121                 return false;
29122                 
29123             }, this);
29124             
29125             if(!match && length == 1){
29126                 return;
29127             }
29128             
29129             if(!match){
29130                 filterPattern(box, length - 1);
29131                 return;
29132             }
29133                 
29134             queue.push(pattern);
29135
29136             box = box.slice(length, box.length);
29137
29138             filterPattern(box, 4);
29139
29140             return;
29141             
29142         }
29143         
29144         Roo.each(boxes, function(box, k){
29145             
29146             if(!box.length){
29147                 return;
29148             }
29149             
29150             if(box.length == 1){
29151                 queue.push(box);
29152                 return;
29153             }
29154             
29155             filterPattern(box, 4);
29156             
29157         }, this);
29158         
29159         this._processVerticalLayoutQueue( queue, isInstant );
29160         
29161     },
29162     
29163 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29164 //    {
29165 //        if ( !items || !items.length ) {
29166 //            return;
29167 //        }
29168 //
29169 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29170 //        
29171 //    },
29172     
29173     _horizontalLayoutItems : function ( items , isInstant)
29174     {
29175         if ( !items || !items.length || items.length < 3) {
29176             return;
29177         }
29178         
29179         items.reverse();
29180         
29181         var eItems = items.slice(0, 3);
29182         
29183         items = items.slice(3, items.length);
29184         
29185         var standard = [
29186             ['xs', 'xs', 'xs', 'wide'],
29187             ['xs', 'xs', 'wide'],
29188             ['xs', 'xs', 'sm'],
29189             ['xs', 'xs', 'xs'],
29190             ['xs', 'wide'],
29191             ['xs', 'sm'],
29192             ['xs', 'xs'],
29193             ['xs'],
29194             
29195             ['sm', 'xs', 'xs'],
29196             ['sm', 'xs'],
29197             ['sm'],
29198             
29199             ['wide', 'xs', 'xs', 'xs'],
29200             ['wide', 'xs', 'xs'],
29201             ['wide', 'xs'],
29202             ['wide'],
29203             
29204             ['wide-thin']
29205         ];
29206         
29207         var queue = [];
29208         
29209         var boxes = [];
29210         
29211         var box = [];
29212         
29213         Roo.each(items, function(item, k){
29214             
29215             switch (item.size) {
29216                 case 'md' :
29217                 case 'md-left' :
29218                 case 'md-right' :
29219                 case 'tall' :
29220                     
29221                     if(box.length){
29222                         boxes.push(box);
29223                         box = [];
29224                     }
29225                     
29226                     boxes.push([item]);
29227                     
29228                     break;
29229                     
29230                 case 'xs' :
29231                 case 'sm' :
29232                 case 'wide' :
29233                 case 'wide-thin' :
29234                     
29235                     box.push(item);
29236                     
29237                     break;
29238                 default :
29239                     break;
29240                     
29241             }
29242             
29243         }, this);
29244         
29245         if(box.length){
29246             boxes.push(box);
29247             box = [];
29248         }
29249         
29250         var filterPattern = function(box, length)
29251         {
29252             if(!box.length){
29253                 return;
29254             }
29255             
29256             var match = false;
29257             
29258             var pattern = box.slice(0, length);
29259             
29260             var format = [];
29261             
29262             Roo.each(pattern, function(i){
29263                 format.push(i.size);
29264             }, this);
29265             
29266             Roo.each(standard, function(s){
29267                 
29268                 if(String(s) != String(format)){
29269                     return;
29270                 }
29271                 
29272                 match = true;
29273                 return false;
29274                 
29275             }, this);
29276             
29277             if(!match && length == 1){
29278                 return;
29279             }
29280             
29281             if(!match){
29282                 filterPattern(box, length - 1);
29283                 return;
29284             }
29285                 
29286             queue.push(pattern);
29287
29288             box = box.slice(length, box.length);
29289
29290             filterPattern(box, 4);
29291
29292             return;
29293             
29294         }
29295         
29296         Roo.each(boxes, function(box, k){
29297             
29298             if(!box.length){
29299                 return;
29300             }
29301             
29302             if(box.length == 1){
29303                 queue.push(box);
29304                 return;
29305             }
29306             
29307             filterPattern(box, 4);
29308             
29309         }, this);
29310         
29311         
29312         var prune = [];
29313         
29314         var pos = this.el.getBox(true);
29315         
29316         var minX = pos.x;
29317         
29318         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29319         
29320         var hit_end = false;
29321         
29322         Roo.each(queue, function(box){
29323             
29324             if(hit_end){
29325                 
29326                 Roo.each(box, function(b){
29327                 
29328                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29329                     b.el.hide();
29330
29331                 }, this);
29332
29333                 return;
29334             }
29335             
29336             var mx = 0;
29337             
29338             Roo.each(box, function(b){
29339                 
29340                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29341                 b.el.show();
29342
29343                 mx = Math.max(mx, b.x);
29344                 
29345             }, this);
29346             
29347             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29348             
29349             if(maxX < minX){
29350                 
29351                 Roo.each(box, function(b){
29352                 
29353                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29354                     b.el.hide();
29355                     
29356                 }, this);
29357                 
29358                 hit_end = true;
29359                 
29360                 return;
29361             }
29362             
29363             prune.push(box);
29364             
29365         }, this);
29366         
29367         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29368     },
29369     
29370     /** Sets position of item in DOM
29371     * @param {Element} item
29372     * @param {Number} x - horizontal position
29373     * @param {Number} y - vertical position
29374     * @param {Boolean} isInstant - disables transitions
29375     */
29376     _processVerticalLayoutQueue : function( queue, isInstant )
29377     {
29378         var pos = this.el.getBox(true);
29379         var x = pos.x;
29380         var y = pos.y;
29381         var maxY = [];
29382         
29383         for (var i = 0; i < this.cols; i++){
29384             maxY[i] = pos.y;
29385         }
29386         
29387         Roo.each(queue, function(box, k){
29388             
29389             var col = k % this.cols;
29390             
29391             Roo.each(box, function(b,kk){
29392                 
29393                 b.el.position('absolute');
29394                 
29395                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29396                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29397                 
29398                 if(b.size == 'md-left' || b.size == 'md-right'){
29399                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29400                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29401                 }
29402                 
29403                 b.el.setWidth(width);
29404                 b.el.setHeight(height);
29405                 // iframe?
29406                 b.el.select('iframe',true).setSize(width,height);
29407                 
29408             }, this);
29409             
29410             for (var i = 0; i < this.cols; i++){
29411                 
29412                 if(maxY[i] < maxY[col]){
29413                     col = i;
29414                     continue;
29415                 }
29416                 
29417                 col = Math.min(col, i);
29418                 
29419             }
29420             
29421             x = pos.x + col * (this.colWidth + this.padWidth);
29422             
29423             y = maxY[col];
29424             
29425             var positions = [];
29426             
29427             switch (box.length){
29428                 case 1 :
29429                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29430                     break;
29431                 case 2 :
29432                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29433                     break;
29434                 case 3 :
29435                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29436                     break;
29437                 case 4 :
29438                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29439                     break;
29440                 default :
29441                     break;
29442             }
29443             
29444             Roo.each(box, function(b,kk){
29445                 
29446                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29447                 
29448                 var sz = b.el.getSize();
29449                 
29450                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29451                 
29452             }, this);
29453             
29454         }, this);
29455         
29456         var mY = 0;
29457         
29458         for (var i = 0; i < this.cols; i++){
29459             mY = Math.max(mY, maxY[i]);
29460         }
29461         
29462         this.el.setHeight(mY - pos.y);
29463         
29464     },
29465     
29466 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29467 //    {
29468 //        var pos = this.el.getBox(true);
29469 //        var x = pos.x;
29470 //        var y = pos.y;
29471 //        var maxX = pos.right;
29472 //        
29473 //        var maxHeight = 0;
29474 //        
29475 //        Roo.each(items, function(item, k){
29476 //            
29477 //            var c = k % 2;
29478 //            
29479 //            item.el.position('absolute');
29480 //                
29481 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29482 //
29483 //            item.el.setWidth(width);
29484 //
29485 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29486 //
29487 //            item.el.setHeight(height);
29488 //            
29489 //            if(c == 0){
29490 //                item.el.setXY([x, y], isInstant ? false : true);
29491 //            } else {
29492 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29493 //            }
29494 //            
29495 //            y = y + height + this.alternativePadWidth;
29496 //            
29497 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29498 //            
29499 //        }, this);
29500 //        
29501 //        this.el.setHeight(maxHeight);
29502 //        
29503 //    },
29504     
29505     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29506     {
29507         var pos = this.el.getBox(true);
29508         
29509         var minX = pos.x;
29510         var minY = pos.y;
29511         
29512         var maxX = pos.right;
29513         
29514         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29515         
29516         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29517         
29518         Roo.each(queue, function(box, k){
29519             
29520             Roo.each(box, function(b, kk){
29521                 
29522                 b.el.position('absolute');
29523                 
29524                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29525                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29526                 
29527                 if(b.size == 'md-left' || b.size == 'md-right'){
29528                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29529                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29530                 }
29531                 
29532                 b.el.setWidth(width);
29533                 b.el.setHeight(height);
29534                 
29535             }, this);
29536             
29537             if(!box.length){
29538                 return;
29539             }
29540             
29541             var positions = [];
29542             
29543             switch (box.length){
29544                 case 1 :
29545                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29546                     break;
29547                 case 2 :
29548                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29549                     break;
29550                 case 3 :
29551                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29552                     break;
29553                 case 4 :
29554                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29555                     break;
29556                 default :
29557                     break;
29558             }
29559             
29560             Roo.each(box, function(b,kk){
29561                 
29562                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29563                 
29564                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29565                 
29566             }, this);
29567             
29568         }, this);
29569         
29570     },
29571     
29572     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29573     {
29574         Roo.each(eItems, function(b,k){
29575             
29576             b.size = (k == 0) ? 'sm' : 'xs';
29577             b.x = (k == 0) ? 2 : 1;
29578             b.y = (k == 0) ? 2 : 1;
29579             
29580             b.el.position('absolute');
29581             
29582             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29583                 
29584             b.el.setWidth(width);
29585             
29586             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29587             
29588             b.el.setHeight(height);
29589             
29590         }, this);
29591
29592         var positions = [];
29593         
29594         positions.push({
29595             x : maxX - this.unitWidth * 2 - this.gutter,
29596             y : minY
29597         });
29598         
29599         positions.push({
29600             x : maxX - this.unitWidth,
29601             y : minY + (this.unitWidth + this.gutter) * 2
29602         });
29603         
29604         positions.push({
29605             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29606             y : minY
29607         });
29608         
29609         Roo.each(eItems, function(b,k){
29610             
29611             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29612
29613         }, this);
29614         
29615     },
29616     
29617     getVerticalOneBoxColPositions : function(x, y, box)
29618     {
29619         var pos = [];
29620         
29621         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29622         
29623         if(box[0].size == 'md-left'){
29624             rand = 0;
29625         }
29626         
29627         if(box[0].size == 'md-right'){
29628             rand = 1;
29629         }
29630         
29631         pos.push({
29632             x : x + (this.unitWidth + this.gutter) * rand,
29633             y : y
29634         });
29635         
29636         return pos;
29637     },
29638     
29639     getVerticalTwoBoxColPositions : function(x, y, box)
29640     {
29641         var pos = [];
29642         
29643         if(box[0].size == 'xs'){
29644             
29645             pos.push({
29646                 x : x,
29647                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29648             });
29649
29650             pos.push({
29651                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29652                 y : y
29653             });
29654             
29655             return pos;
29656             
29657         }
29658         
29659         pos.push({
29660             x : x,
29661             y : y
29662         });
29663
29664         pos.push({
29665             x : x + (this.unitWidth + this.gutter) * 2,
29666             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29667         });
29668         
29669         return pos;
29670         
29671     },
29672     
29673     getVerticalThreeBoxColPositions : function(x, y, box)
29674     {
29675         var pos = [];
29676         
29677         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29678             
29679             pos.push({
29680                 x : x,
29681                 y : y
29682             });
29683
29684             pos.push({
29685                 x : x + (this.unitWidth + this.gutter) * 1,
29686                 y : y
29687             });
29688             
29689             pos.push({
29690                 x : x + (this.unitWidth + this.gutter) * 2,
29691                 y : y
29692             });
29693             
29694             return pos;
29695             
29696         }
29697         
29698         if(box[0].size == 'xs' && box[1].size == 'xs'){
29699             
29700             pos.push({
29701                 x : x,
29702                 y : y
29703             });
29704
29705             pos.push({
29706                 x : x,
29707                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29708             });
29709             
29710             pos.push({
29711                 x : x + (this.unitWidth + this.gutter) * 1,
29712                 y : y
29713             });
29714             
29715             return pos;
29716             
29717         }
29718         
29719         pos.push({
29720             x : x,
29721             y : y
29722         });
29723
29724         pos.push({
29725             x : x + (this.unitWidth + this.gutter) * 2,
29726             y : y
29727         });
29728
29729         pos.push({
29730             x : x + (this.unitWidth + this.gutter) * 2,
29731             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29732         });
29733             
29734         return pos;
29735         
29736     },
29737     
29738     getVerticalFourBoxColPositions : function(x, y, box)
29739     {
29740         var pos = [];
29741         
29742         if(box[0].size == 'xs'){
29743             
29744             pos.push({
29745                 x : x,
29746                 y : y
29747             });
29748
29749             pos.push({
29750                 x : x,
29751                 y : y + (this.unitHeight + this.gutter) * 1
29752             });
29753             
29754             pos.push({
29755                 x : x,
29756                 y : y + (this.unitHeight + this.gutter) * 2
29757             });
29758             
29759             pos.push({
29760                 x : x + (this.unitWidth + this.gutter) * 1,
29761                 y : y
29762             });
29763             
29764             return pos;
29765             
29766         }
29767         
29768         pos.push({
29769             x : x,
29770             y : y
29771         });
29772
29773         pos.push({
29774             x : x + (this.unitWidth + this.gutter) * 2,
29775             y : y
29776         });
29777
29778         pos.push({
29779             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29780             y : y + (this.unitHeight + this.gutter) * 1
29781         });
29782
29783         pos.push({
29784             x : x + (this.unitWidth + this.gutter) * 2,
29785             y : y + (this.unitWidth + this.gutter) * 2
29786         });
29787
29788         return pos;
29789         
29790     },
29791     
29792     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29793     {
29794         var pos = [];
29795         
29796         if(box[0].size == 'md-left'){
29797             pos.push({
29798                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29799                 y : minY
29800             });
29801             
29802             return pos;
29803         }
29804         
29805         if(box[0].size == 'md-right'){
29806             pos.push({
29807                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29808                 y : minY + (this.unitWidth + this.gutter) * 1
29809             });
29810             
29811             return pos;
29812         }
29813         
29814         var rand = Math.floor(Math.random() * (4 - box[0].y));
29815         
29816         pos.push({
29817             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29818             y : minY + (this.unitWidth + this.gutter) * rand
29819         });
29820         
29821         return pos;
29822         
29823     },
29824     
29825     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29826     {
29827         var pos = [];
29828         
29829         if(box[0].size == 'xs'){
29830             
29831             pos.push({
29832                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29833                 y : minY
29834             });
29835
29836             pos.push({
29837                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29838                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29839             });
29840             
29841             return pos;
29842             
29843         }
29844         
29845         pos.push({
29846             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29847             y : minY
29848         });
29849
29850         pos.push({
29851             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29852             y : minY + (this.unitWidth + this.gutter) * 2
29853         });
29854         
29855         return pos;
29856         
29857     },
29858     
29859     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29860     {
29861         var pos = [];
29862         
29863         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29864             
29865             pos.push({
29866                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29867                 y : minY
29868             });
29869
29870             pos.push({
29871                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29872                 y : minY + (this.unitWidth + this.gutter) * 1
29873             });
29874             
29875             pos.push({
29876                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29877                 y : minY + (this.unitWidth + this.gutter) * 2
29878             });
29879             
29880             return pos;
29881             
29882         }
29883         
29884         if(box[0].size == 'xs' && box[1].size == 'xs'){
29885             
29886             pos.push({
29887                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29888                 y : minY
29889             });
29890
29891             pos.push({
29892                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29893                 y : minY
29894             });
29895             
29896             pos.push({
29897                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29898                 y : minY + (this.unitWidth + this.gutter) * 1
29899             });
29900             
29901             return pos;
29902             
29903         }
29904         
29905         pos.push({
29906             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29907             y : minY
29908         });
29909
29910         pos.push({
29911             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29912             y : minY + (this.unitWidth + this.gutter) * 2
29913         });
29914
29915         pos.push({
29916             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29917             y : minY + (this.unitWidth + this.gutter) * 2
29918         });
29919             
29920         return pos;
29921         
29922     },
29923     
29924     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29925     {
29926         var pos = [];
29927         
29928         if(box[0].size == 'xs'){
29929             
29930             pos.push({
29931                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29932                 y : minY
29933             });
29934
29935             pos.push({
29936                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29937                 y : minY
29938             });
29939             
29940             pos.push({
29941                 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),
29942                 y : minY
29943             });
29944             
29945             pos.push({
29946                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29947                 y : minY + (this.unitWidth + this.gutter) * 1
29948             });
29949             
29950             return pos;
29951             
29952         }
29953         
29954         pos.push({
29955             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29956             y : minY
29957         });
29958         
29959         pos.push({
29960             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29961             y : minY + (this.unitWidth + this.gutter) * 2
29962         });
29963         
29964         pos.push({
29965             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29966             y : minY + (this.unitWidth + this.gutter) * 2
29967         });
29968         
29969         pos.push({
29970             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),
29971             y : minY + (this.unitWidth + this.gutter) * 2
29972         });
29973
29974         return pos;
29975         
29976     }
29977     
29978 });
29979
29980  
29981
29982  /**
29983  *
29984  * This is based on 
29985  * http://masonry.desandro.com
29986  *
29987  * The idea is to render all the bricks based on vertical width...
29988  *
29989  * The original code extends 'outlayer' - we might need to use that....
29990  * 
29991  */
29992
29993
29994 /**
29995  * @class Roo.bootstrap.LayoutMasonryAuto
29996  * @extends Roo.bootstrap.Component
29997  * Bootstrap Layout Masonry class
29998  * 
29999  * @constructor
30000  * Create a new Element
30001  * @param {Object} config The config object
30002  */
30003
30004 Roo.bootstrap.LayoutMasonryAuto = function(config){
30005     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30006 };
30007
30008 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30009     
30010       /**
30011      * @cfg {Boolean} isFitWidth  - resize the width..
30012      */   
30013     isFitWidth : false,  // options..
30014     /**
30015      * @cfg {Boolean} isOriginLeft = left align?
30016      */   
30017     isOriginLeft : true,
30018     /**
30019      * @cfg {Boolean} isOriginTop = top align?
30020      */   
30021     isOriginTop : false,
30022     /**
30023      * @cfg {Boolean} isLayoutInstant = no animation?
30024      */   
30025     isLayoutInstant : false, // needed?
30026     /**
30027      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30028      */   
30029     isResizingContainer : true,
30030     /**
30031      * @cfg {Number} columnWidth  width of the columns 
30032      */   
30033     
30034     columnWidth : 0,
30035     
30036     /**
30037      * @cfg {Number} maxCols maximum number of columns
30038      */   
30039     
30040     maxCols: 0,
30041     /**
30042      * @cfg {Number} padHeight padding below box..
30043      */   
30044     
30045     padHeight : 10, 
30046     
30047     /**
30048      * @cfg {Boolean} isAutoInitial defalut true
30049      */   
30050     
30051     isAutoInitial : true, 
30052     
30053     // private?
30054     gutter : 0,
30055     
30056     containerWidth: 0,
30057     initialColumnWidth : 0,
30058     currentSize : null,
30059     
30060     colYs : null, // array.
30061     maxY : 0,
30062     padWidth: 10,
30063     
30064     
30065     tag: 'div',
30066     cls: '',
30067     bricks: null, //CompositeElement
30068     cols : 0, // array?
30069     // element : null, // wrapped now this.el
30070     _isLayoutInited : null, 
30071     
30072     
30073     getAutoCreate : function(){
30074         
30075         var cfg = {
30076             tag: this.tag,
30077             cls: 'blog-masonary-wrapper ' + this.cls,
30078             cn : {
30079                 cls : 'mas-boxes masonary'
30080             }
30081         };
30082         
30083         return cfg;
30084     },
30085     
30086     getChildContainer: function( )
30087     {
30088         if (this.boxesEl) {
30089             return this.boxesEl;
30090         }
30091         
30092         this.boxesEl = this.el.select('.mas-boxes').first();
30093         
30094         return this.boxesEl;
30095     },
30096     
30097     
30098     initEvents : function()
30099     {
30100         var _this = this;
30101         
30102         if(this.isAutoInitial){
30103             Roo.log('hook children rendered');
30104             this.on('childrenrendered', function() {
30105                 Roo.log('children rendered');
30106                 _this.initial();
30107             } ,this);
30108         }
30109         
30110     },
30111     
30112     initial : function()
30113     {
30114         this.reloadItems();
30115
30116         this.currentSize = this.el.getBox(true);
30117
30118         /// was window resize... - let's see if this works..
30119         Roo.EventManager.onWindowResize(this.resize, this); 
30120
30121         if(!this.isAutoInitial){
30122             this.layout();
30123             return;
30124         }
30125         
30126         this.layout.defer(500,this);
30127     },
30128     
30129     reloadItems: function()
30130     {
30131         this.bricks = this.el.select('.masonry-brick', true);
30132         
30133         this.bricks.each(function(b) {
30134             //Roo.log(b.getSize());
30135             if (!b.attr('originalwidth')) {
30136                 b.attr('originalwidth',  b.getSize().width);
30137             }
30138             
30139         });
30140         
30141         Roo.log(this.bricks.elements.length);
30142     },
30143     
30144     resize : function()
30145     {
30146         Roo.log('resize');
30147         var cs = this.el.getBox(true);
30148         
30149         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30150             Roo.log("no change in with or X");
30151             return;
30152         }
30153         this.currentSize = cs;
30154         this.layout();
30155     },
30156     
30157     layout : function()
30158     {
30159          Roo.log('layout');
30160         this._resetLayout();
30161         //this._manageStamps();
30162       
30163         // don't animate first layout
30164         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30165         this.layoutItems( isInstant );
30166       
30167         // flag for initalized
30168         this._isLayoutInited = true;
30169     },
30170     
30171     layoutItems : function( isInstant )
30172     {
30173         //var items = this._getItemsForLayout( this.items );
30174         // original code supports filtering layout items.. we just ignore it..
30175         
30176         this._layoutItems( this.bricks , isInstant );
30177       
30178         this._postLayout();
30179     },
30180     _layoutItems : function ( items , isInstant)
30181     {
30182        //this.fireEvent( 'layout', this, items );
30183     
30184
30185         if ( !items || !items.elements.length ) {
30186           // no items, emit event with empty array
30187             return;
30188         }
30189
30190         var queue = [];
30191         items.each(function(item) {
30192             Roo.log("layout item");
30193             Roo.log(item);
30194             // get x/y object from method
30195             var position = this._getItemLayoutPosition( item );
30196             // enqueue
30197             position.item = item;
30198             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30199             queue.push( position );
30200         }, this);
30201       
30202         this._processLayoutQueue( queue );
30203     },
30204     /** Sets position of item in DOM
30205     * @param {Element} item
30206     * @param {Number} x - horizontal position
30207     * @param {Number} y - vertical position
30208     * @param {Boolean} isInstant - disables transitions
30209     */
30210     _processLayoutQueue : function( queue )
30211     {
30212         for ( var i=0, len = queue.length; i < len; i++ ) {
30213             var obj = queue[i];
30214             obj.item.position('absolute');
30215             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30216         }
30217     },
30218       
30219     
30220     /**
30221     * Any logic you want to do after each layout,
30222     * i.e. size the container
30223     */
30224     _postLayout : function()
30225     {
30226         this.resizeContainer();
30227     },
30228     
30229     resizeContainer : function()
30230     {
30231         if ( !this.isResizingContainer ) {
30232             return;
30233         }
30234         var size = this._getContainerSize();
30235         if ( size ) {
30236             this.el.setSize(size.width,size.height);
30237             this.boxesEl.setSize(size.width,size.height);
30238         }
30239     },
30240     
30241     
30242     
30243     _resetLayout : function()
30244     {
30245         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30246         this.colWidth = this.el.getWidth();
30247         //this.gutter = this.el.getWidth(); 
30248         
30249         this.measureColumns();
30250
30251         // reset column Y
30252         var i = this.cols;
30253         this.colYs = [];
30254         while (i--) {
30255             this.colYs.push( 0 );
30256         }
30257     
30258         this.maxY = 0;
30259     },
30260
30261     measureColumns : function()
30262     {
30263         this.getContainerWidth();
30264       // if columnWidth is 0, default to outerWidth of first item
30265         if ( !this.columnWidth ) {
30266             var firstItem = this.bricks.first();
30267             Roo.log(firstItem);
30268             this.columnWidth  = this.containerWidth;
30269             if (firstItem && firstItem.attr('originalwidth') ) {
30270                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30271             }
30272             // columnWidth fall back to item of first element
30273             Roo.log("set column width?");
30274                         this.initialColumnWidth = this.columnWidth  ;
30275
30276             // if first elem has no width, default to size of container
30277             
30278         }
30279         
30280         
30281         if (this.initialColumnWidth) {
30282             this.columnWidth = this.initialColumnWidth;
30283         }
30284         
30285         
30286             
30287         // column width is fixed at the top - however if container width get's smaller we should
30288         // reduce it...
30289         
30290         // this bit calcs how man columns..
30291             
30292         var columnWidth = this.columnWidth += this.gutter;
30293       
30294         // calculate columns
30295         var containerWidth = this.containerWidth + this.gutter;
30296         
30297         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30298         // fix rounding errors, typically with gutters
30299         var excess = columnWidth - containerWidth % columnWidth;
30300         
30301         
30302         // if overshoot is less than a pixel, round up, otherwise floor it
30303         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30304         cols = Math[ mathMethod ]( cols );
30305         this.cols = Math.max( cols, 1 );
30306         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30307         
30308          // padding positioning..
30309         var totalColWidth = this.cols * this.columnWidth;
30310         var padavail = this.containerWidth - totalColWidth;
30311         // so for 2 columns - we need 3 'pads'
30312         
30313         var padNeeded = (1+this.cols) * this.padWidth;
30314         
30315         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30316         
30317         this.columnWidth += padExtra
30318         //this.padWidth = Math.floor(padavail /  ( this.cols));
30319         
30320         // adjust colum width so that padding is fixed??
30321         
30322         // we have 3 columns ... total = width * 3
30323         // we have X left over... that should be used by 
30324         
30325         //if (this.expandC) {
30326             
30327         //}
30328         
30329         
30330         
30331     },
30332     
30333     getContainerWidth : function()
30334     {
30335        /* // container is parent if fit width
30336         var container = this.isFitWidth ? this.element.parentNode : this.element;
30337         // check that this.size and size are there
30338         // IE8 triggers resize on body size change, so they might not be
30339         
30340         var size = getSize( container );  //FIXME
30341         this.containerWidth = size && size.innerWidth; //FIXME
30342         */
30343          
30344         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30345         
30346     },
30347     
30348     _getItemLayoutPosition : function( item )  // what is item?
30349     {
30350         // we resize the item to our columnWidth..
30351       
30352         item.setWidth(this.columnWidth);
30353         item.autoBoxAdjust  = false;
30354         
30355         var sz = item.getSize();
30356  
30357         // how many columns does this brick span
30358         var remainder = this.containerWidth % this.columnWidth;
30359         
30360         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30361         // round if off by 1 pixel, otherwise use ceil
30362         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30363         colSpan = Math.min( colSpan, this.cols );
30364         
30365         // normally this should be '1' as we dont' currently allow multi width columns..
30366         
30367         var colGroup = this._getColGroup( colSpan );
30368         // get the minimum Y value from the columns
30369         var minimumY = Math.min.apply( Math, colGroup );
30370         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30371         
30372         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30373          
30374         // position the brick
30375         var position = {
30376             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30377             y: this.currentSize.y + minimumY + this.padHeight
30378         };
30379         
30380         Roo.log(position);
30381         // apply setHeight to necessary columns
30382         var setHeight = minimumY + sz.height + this.padHeight;
30383         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30384         
30385         var setSpan = this.cols + 1 - colGroup.length;
30386         for ( var i = 0; i < setSpan; i++ ) {
30387           this.colYs[ shortColIndex + i ] = setHeight ;
30388         }
30389       
30390         return position;
30391     },
30392     
30393     /**
30394      * @param {Number} colSpan - number of columns the element spans
30395      * @returns {Array} colGroup
30396      */
30397     _getColGroup : function( colSpan )
30398     {
30399         if ( colSpan < 2 ) {
30400           // if brick spans only one column, use all the column Ys
30401           return this.colYs;
30402         }
30403       
30404         var colGroup = [];
30405         // how many different places could this brick fit horizontally
30406         var groupCount = this.cols + 1 - colSpan;
30407         // for each group potential horizontal position
30408         for ( var i = 0; i < groupCount; i++ ) {
30409           // make an array of colY values for that one group
30410           var groupColYs = this.colYs.slice( i, i + colSpan );
30411           // and get the max value of the array
30412           colGroup[i] = Math.max.apply( Math, groupColYs );
30413         }
30414         return colGroup;
30415     },
30416     /*
30417     _manageStamp : function( stamp )
30418     {
30419         var stampSize =  stamp.getSize();
30420         var offset = stamp.getBox();
30421         // get the columns that this stamp affects
30422         var firstX = this.isOriginLeft ? offset.x : offset.right;
30423         var lastX = firstX + stampSize.width;
30424         var firstCol = Math.floor( firstX / this.columnWidth );
30425         firstCol = Math.max( 0, firstCol );
30426         
30427         var lastCol = Math.floor( lastX / this.columnWidth );
30428         // lastCol should not go over if multiple of columnWidth #425
30429         lastCol -= lastX % this.columnWidth ? 0 : 1;
30430         lastCol = Math.min( this.cols - 1, lastCol );
30431         
30432         // set colYs to bottom of the stamp
30433         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30434             stampSize.height;
30435             
30436         for ( var i = firstCol; i <= lastCol; i++ ) {
30437           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30438         }
30439     },
30440     */
30441     
30442     _getContainerSize : function()
30443     {
30444         this.maxY = Math.max.apply( Math, this.colYs );
30445         var size = {
30446             height: this.maxY
30447         };
30448       
30449         if ( this.isFitWidth ) {
30450             size.width = this._getContainerFitWidth();
30451         }
30452       
30453         return size;
30454     },
30455     
30456     _getContainerFitWidth : function()
30457     {
30458         var unusedCols = 0;
30459         // count unused columns
30460         var i = this.cols;
30461         while ( --i ) {
30462           if ( this.colYs[i] !== 0 ) {
30463             break;
30464           }
30465           unusedCols++;
30466         }
30467         // fit container to columns that have been used
30468         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30469     },
30470     
30471     needsResizeLayout : function()
30472     {
30473         var previousWidth = this.containerWidth;
30474         this.getContainerWidth();
30475         return previousWidth !== this.containerWidth;
30476     }
30477  
30478 });
30479
30480  
30481
30482  /*
30483  * - LGPL
30484  *
30485  * element
30486  * 
30487  */
30488
30489 /**
30490  * @class Roo.bootstrap.MasonryBrick
30491  * @extends Roo.bootstrap.Component
30492  * Bootstrap MasonryBrick class
30493  * 
30494  * @constructor
30495  * Create a new MasonryBrick
30496  * @param {Object} config The config object
30497  */
30498
30499 Roo.bootstrap.MasonryBrick = function(config){
30500     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30501     
30502     this.addEvents({
30503         // raw events
30504         /**
30505          * @event click
30506          * When a MasonryBrick is clcik
30507          * @param {Roo.bootstrap.MasonryBrick} this
30508          * @param {Roo.EventObject} e
30509          */
30510         "click" : true
30511     });
30512 };
30513
30514 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30515     
30516     /**
30517      * @cfg {String} title
30518      */   
30519     title : '',
30520     /**
30521      * @cfg {String} html
30522      */   
30523     html : '',
30524     /**
30525      * @cfg {String} bgimage
30526      */   
30527     bgimage : '',
30528     /**
30529      * @cfg {String} videourl
30530      */   
30531     videourl : '',
30532     /**
30533      * @cfg {String} cls
30534      */   
30535     cls : '',
30536     /**
30537      * @cfg {String} href
30538      */   
30539     href : '',
30540     /**
30541      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30542      */   
30543     size : 'xs',
30544     
30545     /**
30546      * @cfg {String} (center|bottom) placetitle
30547      */   
30548     placetitle : '',
30549     
30550     getAutoCreate : function()
30551     {
30552         var cls = 'masonry-brick';
30553         
30554         if(this.href.length){
30555             cls += ' masonry-brick-link';
30556         }
30557         
30558         if(this.bgimage.length){
30559             cls += ' masonry-brick-image';
30560         }
30561         
30562         if(this.size){
30563             cls += ' masonry-' + this.size + '-brick';
30564         }
30565         
30566         if(this.placetitle.length){
30567             
30568             switch (this.placetitle) {
30569                 case 'center' :
30570                     cls += ' masonry-center-title';
30571                     break;
30572                 case 'bottom' :
30573                     cls += ' masonry-bottom-title';
30574                     break;
30575                 default:
30576                     break;
30577             }
30578             
30579         } else {
30580             if(!this.html.length && !this.bgimage.length){
30581                 cls += ' masonry-center-title';
30582             }
30583
30584             if(!this.html.length && this.bgimage.length){
30585                 cls += ' masonry-bottom-title';
30586             }
30587         }
30588         
30589         if(this.cls){
30590             cls += ' ' + this.cls;
30591         }
30592         
30593         var cfg = {
30594             tag: (this.href.length) ? 'a' : 'div',
30595             cls: cls,
30596             cn: [
30597                 {
30598                     tag: 'div',
30599                     cls: 'masonry-brick-paragraph',
30600                     cn: []
30601                 }
30602             ]
30603         };
30604         
30605         if(this.href.length){
30606             cfg.href = this.href;
30607         }
30608         
30609         var cn = cfg.cn[0].cn;
30610         
30611         if(this.title.length){
30612             cn.push({
30613                 tag: 'h4',
30614                 cls: 'masonry-brick-title',
30615                 html: this.title
30616             });
30617         }
30618         
30619         if(this.html.length){
30620             cn.push({
30621                 tag: 'p',
30622                 cls: 'masonry-brick-text',
30623                 html: this.html
30624             });
30625         }  
30626         if (!this.title.length && !this.html.length) {
30627             cfg.cn[0].cls += ' hide';
30628         }
30629         
30630         if(this.bgimage.length){
30631             cfg.cn.push({
30632                 tag: 'img',
30633                 cls: 'masonry-brick-image-view',
30634                 src: this.bgimage
30635             });
30636         }
30637         if(this.videourl.length){
30638             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30639             // youtube support only?
30640             cfg.cn.push({
30641                 tag: 'iframe',
30642                 cls: 'masonry-brick-image-view',
30643                 src: vurl,
30644                 frameborder : 0,
30645                 allowfullscreen : true
30646             });
30647             
30648             
30649         }
30650         return cfg;
30651         
30652     },
30653     
30654     initEvents: function() 
30655     {
30656         switch (this.size) {
30657             case 'xs' :
30658 //                this.intSize = 1;
30659                 this.x = 1;
30660                 this.y = 1;
30661                 break;
30662             case 'sm' :
30663 //                this.intSize = 2;
30664                 this.x = 2;
30665                 this.y = 2;
30666                 break;
30667             case 'md' :
30668             case 'md-left' :
30669             case 'md-right' :
30670 //                this.intSize = 3;
30671                 this.x = 3;
30672                 this.y = 3;
30673                 break;
30674             case 'tall' :
30675 //                this.intSize = 3;
30676                 this.x = 2;
30677                 this.y = 3;
30678                 break;
30679             case 'wide' :
30680 //                this.intSize = 3;
30681                 this.x = 3;
30682                 this.y = 2;
30683                 break;
30684             case 'wide-thin' :
30685 //                this.intSize = 3;
30686                 this.x = 3;
30687                 this.y = 1;
30688                 break;
30689                         
30690             default :
30691                 break;
30692         }
30693         
30694         
30695         
30696         if(Roo.isTouch){
30697             this.el.on('touchstart', this.onTouchStart, this);
30698             this.el.on('touchmove', this.onTouchMove, this);
30699             this.el.on('touchend', this.onTouchEnd, this);
30700             this.el.on('contextmenu', this.onContextMenu, this);
30701         } else {
30702             this.el.on('mouseenter'  ,this.enter, this);
30703             this.el.on('mouseleave', this.leave, this);
30704         }
30705         
30706         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30707             this.parent().bricks.push(this);   
30708         }
30709         
30710     },
30711     
30712     onClick: function(e, el)
30713     {
30714         if(!Roo.isTouch){
30715             return;
30716         }
30717         
30718         var time = this.endTimer - this.startTimer;
30719         
30720         //alert(time);
30721         
30722         if(time < 1000){
30723             return;
30724         }
30725         
30726         e.preventDefault();
30727     },
30728     
30729     enter: function(e, el)
30730     {
30731         e.preventDefault();
30732         
30733         if(this.bgimage.length && this.html.length){
30734             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30735         }
30736     },
30737     
30738     leave: function(e, el)
30739     {
30740         e.preventDefault();
30741         
30742         if(this.bgimage.length && this.html.length){
30743             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30744         }
30745     },
30746     
30747     onTouchStart: function(e, el)
30748     {
30749 //        e.preventDefault();
30750         
30751         this.touchmoved = false;
30752         
30753         if(!this.bgimage.length || !this.html.length){
30754             return;
30755         }
30756         
30757         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30758         
30759         this.timer = new Date().getTime();
30760         
30761     },
30762     
30763     onTouchMove: function(e, el)
30764     {
30765         this.touchmoved = true;
30766     },
30767     
30768     onContextMenu : function(e,el)
30769     {
30770         e.preventDefault();
30771         e.stopPropagation();
30772         return false;
30773     },
30774     
30775     onTouchEnd: function(e, el)
30776     {
30777 //        e.preventDefault();
30778         
30779         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30780         
30781             this.leave(e,el);
30782             
30783             return;
30784         }
30785         
30786         if(!this.bgimage.length || !this.html.length){
30787             
30788             if(this.href.length){
30789                 window.location.href = this.href;
30790             }
30791             
30792             return;
30793         }
30794         
30795         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30796         
30797         window.location.href = this.href;
30798     }
30799     
30800 });
30801
30802  
30803
30804  /*
30805  * - LGPL
30806  *
30807  * element
30808  * 
30809  */
30810
30811 /**
30812  * @class Roo.bootstrap.Brick
30813  * @extends Roo.bootstrap.Component
30814  * Bootstrap Brick class
30815  * 
30816  * @constructor
30817  * Create a new Brick
30818  * @param {Object} config The config object
30819  */
30820
30821 Roo.bootstrap.Brick = function(config){
30822     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30823     
30824     this.addEvents({
30825         // raw events
30826         /**
30827          * @event click
30828          * When a Brick is click
30829          * @param {Roo.bootstrap.Brick} this
30830          * @param {Roo.EventObject} e
30831          */
30832         "click" : true
30833     });
30834 };
30835
30836 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30837     
30838     /**
30839      * @cfg {String} title
30840      */   
30841     title : '',
30842     /**
30843      * @cfg {String} html
30844      */   
30845     html : '',
30846     /**
30847      * @cfg {String} bgimage
30848      */   
30849     bgimage : '',
30850     /**
30851      * @cfg {String} cls
30852      */   
30853     cls : '',
30854     /**
30855      * @cfg {String} href
30856      */   
30857     href : '',
30858     /**
30859      * @cfg {String} video
30860      */   
30861     video : '',
30862     /**
30863      * @cfg {Boolean} square
30864      */   
30865     square : true,
30866     
30867     getAutoCreate : function()
30868     {
30869         var cls = 'roo-brick';
30870         
30871         if(this.href.length){
30872             cls += ' roo-brick-link';
30873         }
30874         
30875         if(this.bgimage.length){
30876             cls += ' roo-brick-image';
30877         }
30878         
30879         if(!this.html.length && !this.bgimage.length){
30880             cls += ' roo-brick-center-title';
30881         }
30882         
30883         if(!this.html.length && this.bgimage.length){
30884             cls += ' roo-brick-bottom-title';
30885         }
30886         
30887         if(this.cls){
30888             cls += ' ' + this.cls;
30889         }
30890         
30891         var cfg = {
30892             tag: (this.href.length) ? 'a' : 'div',
30893             cls: cls,
30894             cn: [
30895                 {
30896                     tag: 'div',
30897                     cls: 'roo-brick-paragraph',
30898                     cn: []
30899                 }
30900             ]
30901         };
30902         
30903         if(this.href.length){
30904             cfg.href = this.href;
30905         }
30906         
30907         var cn = cfg.cn[0].cn;
30908         
30909         if(this.title.length){
30910             cn.push({
30911                 tag: 'h4',
30912                 cls: 'roo-brick-title',
30913                 html: this.title
30914             });
30915         }
30916         
30917         if(this.html.length){
30918             cn.push({
30919                 tag: 'p',
30920                 cls: 'roo-brick-text',
30921                 html: this.html
30922             });
30923         } else {
30924             cn.cls += ' hide';
30925         }
30926         
30927         if(this.bgimage.length){
30928             cfg.cn.push({
30929                 tag: 'img',
30930                 cls: 'roo-brick-image-view',
30931                 src: this.bgimage
30932             });
30933         }
30934         
30935         return cfg;
30936     },
30937     
30938     initEvents: function() 
30939     {
30940         if(this.title.length || this.html.length){
30941             this.el.on('mouseenter'  ,this.enter, this);
30942             this.el.on('mouseleave', this.leave, this);
30943         }
30944         
30945         
30946         Roo.EventManager.onWindowResize(this.resize, this); 
30947         
30948         this.resize();
30949     },
30950     
30951     resize : function()
30952     {
30953         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30954         
30955         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30956 //        paragraph.setHeight(paragraph.getWidth());
30957         
30958         if(this.bgimage.length){
30959             var image = this.el.select('.roo-brick-image-view', true).first();
30960             image.setWidth(paragraph.getWidth());
30961             image.setHeight(paragraph.getWidth());
30962         }
30963         
30964     },
30965     
30966     enter: function(e, el)
30967     {
30968         e.preventDefault();
30969         
30970         if(this.bgimage.length){
30971             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30972             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30973         }
30974     },
30975     
30976     leave: function(e, el)
30977     {
30978         e.preventDefault();
30979         
30980         if(this.bgimage.length){
30981             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30982             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30983         }
30984     }
30985     
30986 });
30987
30988  
30989
30990  /*
30991  * Based on:
30992  * Ext JS Library 1.1.1
30993  * Copyright(c) 2006-2007, Ext JS, LLC.
30994  *
30995  * Originally Released Under LGPL - original licence link has changed is not relivant.
30996  *
30997  * Fork - LGPL
30998  * <script type="text/javascript">
30999  */
31000
31001
31002 /**
31003  * @class Roo.bootstrap.SplitBar
31004  * @extends Roo.util.Observable
31005  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31006  * <br><br>
31007  * Usage:
31008  * <pre><code>
31009 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31010                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31011 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31012 split.minSize = 100;
31013 split.maxSize = 600;
31014 split.animate = true;
31015 split.on('moved', splitterMoved);
31016 </code></pre>
31017  * @constructor
31018  * Create a new SplitBar
31019  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31020  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31021  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31022  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31023                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31024                         position of the SplitBar).
31025  */
31026 Roo.bootstrap.SplitBar = function(cfg){
31027     
31028     /** @private */
31029     
31030     //{
31031     //  dragElement : elm
31032     //  resizingElement: el,
31033         // optional..
31034     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31035     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31036         // existingProxy ???
31037     //}
31038     
31039     this.el = Roo.get(cfg.dragElement, true);
31040     this.el.dom.unselectable = "on";
31041     /** @private */
31042     this.resizingEl = Roo.get(cfg.resizingElement, true);
31043
31044     /**
31045      * @private
31046      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31047      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31048      * @type Number
31049      */
31050     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31051     
31052     /**
31053      * The minimum size of the resizing element. (Defaults to 0)
31054      * @type Number
31055      */
31056     this.minSize = 0;
31057     
31058     /**
31059      * The maximum size of the resizing element. (Defaults to 2000)
31060      * @type Number
31061      */
31062     this.maxSize = 2000;
31063     
31064     /**
31065      * Whether to animate the transition to the new size
31066      * @type Boolean
31067      */
31068     this.animate = false;
31069     
31070     /**
31071      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31072      * @type Boolean
31073      */
31074     this.useShim = false;
31075     
31076     /** @private */
31077     this.shim = null;
31078     
31079     if(!cfg.existingProxy){
31080         /** @private */
31081         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31082     }else{
31083         this.proxy = Roo.get(cfg.existingProxy).dom;
31084     }
31085     /** @private */
31086     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31087     
31088     /** @private */
31089     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31090     
31091     /** @private */
31092     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31093     
31094     /** @private */
31095     this.dragSpecs = {};
31096     
31097     /**
31098      * @private The adapter to use to positon and resize elements
31099      */
31100     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31101     this.adapter.init(this);
31102     
31103     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31104         /** @private */
31105         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31106         this.el.addClass("roo-splitbar-h");
31107     }else{
31108         /** @private */
31109         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31110         this.el.addClass("roo-splitbar-v");
31111     }
31112     
31113     this.addEvents({
31114         /**
31115          * @event resize
31116          * Fires when the splitter is moved (alias for {@link #event-moved})
31117          * @param {Roo.bootstrap.SplitBar} this
31118          * @param {Number} newSize the new width or height
31119          */
31120         "resize" : true,
31121         /**
31122          * @event moved
31123          * Fires when the splitter is moved
31124          * @param {Roo.bootstrap.SplitBar} this
31125          * @param {Number} newSize the new width or height
31126          */
31127         "moved" : true,
31128         /**
31129          * @event beforeresize
31130          * Fires before the splitter is dragged
31131          * @param {Roo.bootstrap.SplitBar} this
31132          */
31133         "beforeresize" : true,
31134
31135         "beforeapply" : true
31136     });
31137
31138     Roo.util.Observable.call(this);
31139 };
31140
31141 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31142     onStartProxyDrag : function(x, y){
31143         this.fireEvent("beforeresize", this);
31144         if(!this.overlay){
31145             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31146             o.unselectable();
31147             o.enableDisplayMode("block");
31148             // all splitbars share the same overlay
31149             Roo.bootstrap.SplitBar.prototype.overlay = o;
31150         }
31151         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31152         this.overlay.show();
31153         Roo.get(this.proxy).setDisplayed("block");
31154         var size = this.adapter.getElementSize(this);
31155         this.activeMinSize = this.getMinimumSize();;
31156         this.activeMaxSize = this.getMaximumSize();;
31157         var c1 = size - this.activeMinSize;
31158         var c2 = Math.max(this.activeMaxSize - size, 0);
31159         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31160             this.dd.resetConstraints();
31161             this.dd.setXConstraint(
31162                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31163                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31164             );
31165             this.dd.setYConstraint(0, 0);
31166         }else{
31167             this.dd.resetConstraints();
31168             this.dd.setXConstraint(0, 0);
31169             this.dd.setYConstraint(
31170                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31171                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31172             );
31173          }
31174         this.dragSpecs.startSize = size;
31175         this.dragSpecs.startPoint = [x, y];
31176         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31177     },
31178     
31179     /** 
31180      * @private Called after the drag operation by the DDProxy
31181      */
31182     onEndProxyDrag : function(e){
31183         Roo.get(this.proxy).setDisplayed(false);
31184         var endPoint = Roo.lib.Event.getXY(e);
31185         if(this.overlay){
31186             this.overlay.hide();
31187         }
31188         var newSize;
31189         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31190             newSize = this.dragSpecs.startSize + 
31191                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31192                     endPoint[0] - this.dragSpecs.startPoint[0] :
31193                     this.dragSpecs.startPoint[0] - endPoint[0]
31194                 );
31195         }else{
31196             newSize = this.dragSpecs.startSize + 
31197                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31198                     endPoint[1] - this.dragSpecs.startPoint[1] :
31199                     this.dragSpecs.startPoint[1] - endPoint[1]
31200                 );
31201         }
31202         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31203         if(newSize != this.dragSpecs.startSize){
31204             if(this.fireEvent('beforeapply', this, newSize) !== false){
31205                 this.adapter.setElementSize(this, newSize);
31206                 this.fireEvent("moved", this, newSize);
31207                 this.fireEvent("resize", this, newSize);
31208             }
31209         }
31210     },
31211     
31212     /**
31213      * Get the adapter this SplitBar uses
31214      * @return The adapter object
31215      */
31216     getAdapter : function(){
31217         return this.adapter;
31218     },
31219     
31220     /**
31221      * Set the adapter this SplitBar uses
31222      * @param {Object} adapter A SplitBar adapter object
31223      */
31224     setAdapter : function(adapter){
31225         this.adapter = adapter;
31226         this.adapter.init(this);
31227     },
31228     
31229     /**
31230      * Gets the minimum size for the resizing element
31231      * @return {Number} The minimum size
31232      */
31233     getMinimumSize : function(){
31234         return this.minSize;
31235     },
31236     
31237     /**
31238      * Sets the minimum size for the resizing element
31239      * @param {Number} minSize The minimum size
31240      */
31241     setMinimumSize : function(minSize){
31242         this.minSize = minSize;
31243     },
31244     
31245     /**
31246      * Gets the maximum size for the resizing element
31247      * @return {Number} The maximum size
31248      */
31249     getMaximumSize : function(){
31250         return this.maxSize;
31251     },
31252     
31253     /**
31254      * Sets the maximum size for the resizing element
31255      * @param {Number} maxSize The maximum size
31256      */
31257     setMaximumSize : function(maxSize){
31258         this.maxSize = maxSize;
31259     },
31260     
31261     /**
31262      * Sets the initialize size for the resizing element
31263      * @param {Number} size The initial size
31264      */
31265     setCurrentSize : function(size){
31266         var oldAnimate = this.animate;
31267         this.animate = false;
31268         this.adapter.setElementSize(this, size);
31269         this.animate = oldAnimate;
31270     },
31271     
31272     /**
31273      * Destroy this splitbar. 
31274      * @param {Boolean} removeEl True to remove the element
31275      */
31276     destroy : function(removeEl){
31277         if(this.shim){
31278             this.shim.remove();
31279         }
31280         this.dd.unreg();
31281         this.proxy.parentNode.removeChild(this.proxy);
31282         if(removeEl){
31283             this.el.remove();
31284         }
31285     }
31286 });
31287
31288 /**
31289  * @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.
31290  */
31291 Roo.bootstrap.SplitBar.createProxy = function(dir){
31292     var proxy = new Roo.Element(document.createElement("div"));
31293     proxy.unselectable();
31294     var cls = 'roo-splitbar-proxy';
31295     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31296     document.body.appendChild(proxy.dom);
31297     return proxy.dom;
31298 };
31299
31300 /** 
31301  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31302  * Default Adapter. It assumes the splitter and resizing element are not positioned
31303  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31304  */
31305 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31306 };
31307
31308 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31309     // do nothing for now
31310     init : function(s){
31311     
31312     },
31313     /**
31314      * Called before drag operations to get the current size of the resizing element. 
31315      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31316      */
31317      getElementSize : function(s){
31318         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31319             return s.resizingEl.getWidth();
31320         }else{
31321             return s.resizingEl.getHeight();
31322         }
31323     },
31324     
31325     /**
31326      * Called after drag operations to set the size of the resizing element.
31327      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31328      * @param {Number} newSize The new size to set
31329      * @param {Function} onComplete A function to be invoked when resizing is complete
31330      */
31331     setElementSize : function(s, newSize, onComplete){
31332         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31333             if(!s.animate){
31334                 s.resizingEl.setWidth(newSize);
31335                 if(onComplete){
31336                     onComplete(s, newSize);
31337                 }
31338             }else{
31339                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31340             }
31341         }else{
31342             
31343             if(!s.animate){
31344                 s.resizingEl.setHeight(newSize);
31345                 if(onComplete){
31346                     onComplete(s, newSize);
31347                 }
31348             }else{
31349                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31350             }
31351         }
31352     }
31353 };
31354
31355 /** 
31356  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31357  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31358  * Adapter that  moves the splitter element to align with the resized sizing element. 
31359  * Used with an absolute positioned SplitBar.
31360  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31361  * document.body, make sure you assign an id to the body element.
31362  */
31363 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31364     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31365     this.container = Roo.get(container);
31366 };
31367
31368 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31369     init : function(s){
31370         this.basic.init(s);
31371     },
31372     
31373     getElementSize : function(s){
31374         return this.basic.getElementSize(s);
31375     },
31376     
31377     setElementSize : function(s, newSize, onComplete){
31378         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31379     },
31380     
31381     moveSplitter : function(s){
31382         var yes = Roo.bootstrap.SplitBar;
31383         switch(s.placement){
31384             case yes.LEFT:
31385                 s.el.setX(s.resizingEl.getRight());
31386                 break;
31387             case yes.RIGHT:
31388                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31389                 break;
31390             case yes.TOP:
31391                 s.el.setY(s.resizingEl.getBottom());
31392                 break;
31393             case yes.BOTTOM:
31394                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31395                 break;
31396         }
31397     }
31398 };
31399
31400 /**
31401  * Orientation constant - Create a vertical SplitBar
31402  * @static
31403  * @type Number
31404  */
31405 Roo.bootstrap.SplitBar.VERTICAL = 1;
31406
31407 /**
31408  * Orientation constant - Create a horizontal SplitBar
31409  * @static
31410  * @type Number
31411  */
31412 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31413
31414 /**
31415  * Placement constant - The resizing element is to the left of the splitter element
31416  * @static
31417  * @type Number
31418  */
31419 Roo.bootstrap.SplitBar.LEFT = 1;
31420
31421 /**
31422  * Placement constant - The resizing element is to the right of the splitter element
31423  * @static
31424  * @type Number
31425  */
31426 Roo.bootstrap.SplitBar.RIGHT = 2;
31427
31428 /**
31429  * Placement constant - The resizing element is positioned above the splitter element
31430  * @static
31431  * @type Number
31432  */
31433 Roo.bootstrap.SplitBar.TOP = 3;
31434
31435 /**
31436  * Placement constant - The resizing element is positioned under splitter element
31437  * @static
31438  * @type Number
31439  */
31440 Roo.bootstrap.SplitBar.BOTTOM = 4;
31441 Roo.namespace("Roo.bootstrap.layout");/*
31442  * Based on:
31443  * Ext JS Library 1.1.1
31444  * Copyright(c) 2006-2007, Ext JS, LLC.
31445  *
31446  * Originally Released Under LGPL - original licence link has changed is not relivant.
31447  *
31448  * Fork - LGPL
31449  * <script type="text/javascript">
31450  */
31451  
31452 /**
31453  * @class Roo.bootstrap.layout.Manager
31454  * @extends Roo.bootstrap.Component
31455  * Base class for layout managers.
31456  */
31457 Roo.bootstrap.layout.Manager = function(config)
31458 {
31459     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31460     
31461     
31462      
31463     
31464     
31465     /** false to disable window resize monitoring @type Boolean */
31466     this.monitorWindowResize = true;
31467     this.regions = {};
31468     this.addEvents({
31469         /**
31470          * @event layout
31471          * Fires when a layout is performed. 
31472          * @param {Roo.LayoutManager} this
31473          */
31474         "layout" : true,
31475         /**
31476          * @event regionresized
31477          * Fires when the user resizes a region. 
31478          * @param {Roo.LayoutRegion} region The resized region
31479          * @param {Number} newSize The new size (width for east/west, height for north/south)
31480          */
31481         "regionresized" : true,
31482         /**
31483          * @event regioncollapsed
31484          * Fires when a region is collapsed. 
31485          * @param {Roo.LayoutRegion} region The collapsed region
31486          */
31487         "regioncollapsed" : true,
31488         /**
31489          * @event regionexpanded
31490          * Fires when a region is expanded.  
31491          * @param {Roo.LayoutRegion} region The expanded region
31492          */
31493         "regionexpanded" : true
31494     });
31495     this.updating = false;
31496     
31497     if (config.el) {
31498         this.el = Roo.get(config.el);
31499         this.initEvents();
31500     }
31501     
31502 };
31503
31504 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31505     
31506     
31507     regions : null,
31508     
31509     monitorWindowResize : true,
31510     
31511     
31512     updating : false,
31513     
31514     
31515     onRender : function(ct, position)
31516     {
31517         if(!this.el){
31518             this.el = Roo.get(ct);
31519             this.initEvents();
31520         }
31521     },
31522     
31523     
31524     initEvents: function()
31525     {
31526         
31527         
31528         // ie scrollbar fix
31529         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31530             document.body.scroll = "no";
31531         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31532             this.el.position('relative');
31533         }
31534         this.id = this.el.id;
31535         this.el.addClass("roo-layout-container");
31536         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31537         if(this.el.dom != document.body ) {
31538             this.el.on('resize', this.layout,this);
31539             this.el.on('show', this.layout,this);
31540         }
31541
31542     },
31543     
31544     /**
31545      * Returns true if this layout is currently being updated
31546      * @return {Boolean}
31547      */
31548     isUpdating : function(){
31549         return this.updating; 
31550     },
31551     
31552     /**
31553      * Suspend the LayoutManager from doing auto-layouts while
31554      * making multiple add or remove calls
31555      */
31556     beginUpdate : function(){
31557         this.updating = true;    
31558     },
31559     
31560     /**
31561      * Restore auto-layouts and optionally disable the manager from performing a layout
31562      * @param {Boolean} noLayout true to disable a layout update 
31563      */
31564     endUpdate : function(noLayout){
31565         this.updating = false;
31566         if(!noLayout){
31567             this.layout();
31568         }    
31569     },
31570     
31571     layout: function(){
31572         // abstract...
31573     },
31574     
31575     onRegionResized : function(region, newSize){
31576         this.fireEvent("regionresized", region, newSize);
31577         this.layout();
31578     },
31579     
31580     onRegionCollapsed : function(region){
31581         this.fireEvent("regioncollapsed", region);
31582     },
31583     
31584     onRegionExpanded : function(region){
31585         this.fireEvent("regionexpanded", region);
31586     },
31587         
31588     /**
31589      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31590      * performs box-model adjustments.
31591      * @return {Object} The size as an object {width: (the width), height: (the height)}
31592      */
31593     getViewSize : function()
31594     {
31595         var size;
31596         if(this.el.dom != document.body){
31597             size = this.el.getSize();
31598         }else{
31599             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31600         }
31601         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31602         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31603         return size;
31604     },
31605     
31606     /**
31607      * Returns the Element this layout is bound to.
31608      * @return {Roo.Element}
31609      */
31610     getEl : function(){
31611         return this.el;
31612     },
31613     
31614     /**
31615      * Returns the specified region.
31616      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31617      * @return {Roo.LayoutRegion}
31618      */
31619     getRegion : function(target){
31620         return this.regions[target.toLowerCase()];
31621     },
31622     
31623     onWindowResize : function(){
31624         if(this.monitorWindowResize){
31625             this.layout();
31626         }
31627     }
31628 });/*
31629  * Based on:
31630  * Ext JS Library 1.1.1
31631  * Copyright(c) 2006-2007, Ext JS, LLC.
31632  *
31633  * Originally Released Under LGPL - original licence link has changed is not relivant.
31634  *
31635  * Fork - LGPL
31636  * <script type="text/javascript">
31637  */
31638 /**
31639  * @class Roo.bootstrap.layout.Border
31640  * @extends Roo.bootstrap.layout.Manager
31641  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31642  * please see: examples/bootstrap/nested.html<br><br>
31643  
31644 <b>The container the layout is rendered into can be either the body element or any other element.
31645 If it is not the body element, the container needs to either be an absolute positioned element,
31646 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31647 the container size if it is not the body element.</b>
31648
31649 * @constructor
31650 * Create a new Border
31651 * @param {Object} config Configuration options
31652  */
31653 Roo.bootstrap.layout.Border = function(config){
31654     config = config || {};
31655     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31656     
31657     
31658     
31659     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31660         if(config[region]){
31661             config[region].region = region;
31662             this.addRegion(config[region]);
31663         }
31664     },this);
31665     
31666 };
31667
31668 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31669
31670 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31671     /**
31672      * Creates and adds a new region if it doesn't already exist.
31673      * @param {String} target The target region key (north, south, east, west or center).
31674      * @param {Object} config The regions config object
31675      * @return {BorderLayoutRegion} The new region
31676      */
31677     addRegion : function(config)
31678     {
31679         if(!this.regions[config.region]){
31680             var r = this.factory(config);
31681             this.bindRegion(r);
31682         }
31683         return this.regions[config.region];
31684     },
31685
31686     // private (kinda)
31687     bindRegion : function(r){
31688         this.regions[r.config.region] = r;
31689         
31690         r.on("visibilitychange",    this.layout, this);
31691         r.on("paneladded",          this.layout, this);
31692         r.on("panelremoved",        this.layout, this);
31693         r.on("invalidated",         this.layout, this);
31694         r.on("resized",             this.onRegionResized, this);
31695         r.on("collapsed",           this.onRegionCollapsed, this);
31696         r.on("expanded",            this.onRegionExpanded, this);
31697     },
31698
31699     /**
31700      * Performs a layout update.
31701      */
31702     layout : function()
31703     {
31704         if(this.updating) {
31705             return;
31706         }
31707         var size = this.getViewSize();
31708         var w = size.width;
31709         var h = size.height;
31710         var centerW = w;
31711         var centerH = h;
31712         var centerY = 0;
31713         var centerX = 0;
31714         //var x = 0, y = 0;
31715
31716         var rs = this.regions;
31717         var north = rs["north"];
31718         var south = rs["south"]; 
31719         var west = rs["west"];
31720         var east = rs["east"];
31721         var center = rs["center"];
31722         //if(this.hideOnLayout){ // not supported anymore
31723             //c.el.setStyle("display", "none");
31724         //}
31725         if(north && north.isVisible()){
31726             var b = north.getBox();
31727             var m = north.getMargins();
31728             b.width = w - (m.left+m.right);
31729             b.x = m.left;
31730             b.y = m.top;
31731             centerY = b.height + b.y + m.bottom;
31732             centerH -= centerY;
31733             north.updateBox(this.safeBox(b));
31734         }
31735         if(south && south.isVisible()){
31736             var b = south.getBox();
31737             var m = south.getMargins();
31738             b.width = w - (m.left+m.right);
31739             b.x = m.left;
31740             var totalHeight = (b.height + m.top + m.bottom);
31741             b.y = h - totalHeight + m.top;
31742             centerH -= totalHeight;
31743             south.updateBox(this.safeBox(b));
31744         }
31745         if(west && west.isVisible()){
31746             var b = west.getBox();
31747             var m = west.getMargins();
31748             b.height = centerH - (m.top+m.bottom);
31749             b.x = m.left;
31750             b.y = centerY + m.top;
31751             var totalWidth = (b.width + m.left + m.right);
31752             centerX += totalWidth;
31753             centerW -= totalWidth;
31754             west.updateBox(this.safeBox(b));
31755         }
31756         if(east && east.isVisible()){
31757             var b = east.getBox();
31758             var m = east.getMargins();
31759             b.height = centerH - (m.top+m.bottom);
31760             var totalWidth = (b.width + m.left + m.right);
31761             b.x = w - totalWidth + m.left;
31762             b.y = centerY + m.top;
31763             centerW -= totalWidth;
31764             east.updateBox(this.safeBox(b));
31765         }
31766         if(center){
31767             var m = center.getMargins();
31768             var centerBox = {
31769                 x: centerX + m.left,
31770                 y: centerY + m.top,
31771                 width: centerW - (m.left+m.right),
31772                 height: centerH - (m.top+m.bottom)
31773             };
31774             //if(this.hideOnLayout){
31775                 //center.el.setStyle("display", "block");
31776             //}
31777             center.updateBox(this.safeBox(centerBox));
31778         }
31779         this.el.repaint();
31780         this.fireEvent("layout", this);
31781     },
31782
31783     // private
31784     safeBox : function(box){
31785         box.width = Math.max(0, box.width);
31786         box.height = Math.max(0, box.height);
31787         return box;
31788     },
31789
31790     /**
31791      * Adds a ContentPanel (or subclass) to this layout.
31792      * @param {String} target The target region key (north, south, east, west or center).
31793      * @param {Roo.ContentPanel} panel The panel to add
31794      * @return {Roo.ContentPanel} The added panel
31795      */
31796     add : function(target, panel){
31797          
31798         target = target.toLowerCase();
31799         return this.regions[target].add(panel);
31800     },
31801
31802     /**
31803      * Remove a ContentPanel (or subclass) to this layout.
31804      * @param {String} target The target region key (north, south, east, west or center).
31805      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31806      * @return {Roo.ContentPanel} The removed panel
31807      */
31808     remove : function(target, panel){
31809         target = target.toLowerCase();
31810         return this.regions[target].remove(panel);
31811     },
31812
31813     /**
31814      * Searches all regions for a panel with the specified id
31815      * @param {String} panelId
31816      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31817      */
31818     findPanel : function(panelId){
31819         var rs = this.regions;
31820         for(var target in rs){
31821             if(typeof rs[target] != "function"){
31822                 var p = rs[target].getPanel(panelId);
31823                 if(p){
31824                     return p;
31825                 }
31826             }
31827         }
31828         return null;
31829     },
31830
31831     /**
31832      * Searches all regions for a panel with the specified id and activates (shows) it.
31833      * @param {String/ContentPanel} panelId The panels id or the panel itself
31834      * @return {Roo.ContentPanel} The shown panel or null
31835      */
31836     showPanel : function(panelId) {
31837       var rs = this.regions;
31838       for(var target in rs){
31839          var r = rs[target];
31840          if(typeof r != "function"){
31841             if(r.hasPanel(panelId)){
31842                return r.showPanel(panelId);
31843             }
31844          }
31845       }
31846       return null;
31847    },
31848
31849    /**
31850      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31851      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31852      */
31853    /*
31854     restoreState : function(provider){
31855         if(!provider){
31856             provider = Roo.state.Manager;
31857         }
31858         var sm = new Roo.LayoutStateManager();
31859         sm.init(this, provider);
31860     },
31861 */
31862  
31863  
31864     /**
31865      * Adds a xtype elements to the layout.
31866      * <pre><code>
31867
31868 layout.addxtype({
31869        xtype : 'ContentPanel',
31870        region: 'west',
31871        items: [ .... ]
31872    }
31873 );
31874
31875 layout.addxtype({
31876         xtype : 'NestedLayoutPanel',
31877         region: 'west',
31878         layout: {
31879            center: { },
31880            west: { }   
31881         },
31882         items : [ ... list of content panels or nested layout panels.. ]
31883    }
31884 );
31885 </code></pre>
31886      * @param {Object} cfg Xtype definition of item to add.
31887      */
31888     addxtype : function(cfg)
31889     {
31890         // basically accepts a pannel...
31891         // can accept a layout region..!?!?
31892         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31893         
31894         
31895         // theory?  children can only be panels??
31896         
31897         //if (!cfg.xtype.match(/Panel$/)) {
31898         //    return false;
31899         //}
31900         var ret = false;
31901         
31902         if (typeof(cfg.region) == 'undefined') {
31903             Roo.log("Failed to add Panel, region was not set");
31904             Roo.log(cfg);
31905             return false;
31906         }
31907         var region = cfg.region;
31908         delete cfg.region;
31909         
31910           
31911         var xitems = [];
31912         if (cfg.items) {
31913             xitems = cfg.items;
31914             delete cfg.items;
31915         }
31916         var nb = false;
31917         
31918         switch(cfg.xtype) 
31919         {
31920             case 'Content':  // ContentPanel (el, cfg)
31921             case 'Scroll':  // ContentPanel (el, cfg)
31922             case 'View': 
31923                 cfg.autoCreate = true;
31924                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31925                 //} else {
31926                 //    var el = this.el.createChild();
31927                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31928                 //}
31929                 
31930                 this.add(region, ret);
31931                 break;
31932             
31933             /*
31934             case 'TreePanel': // our new panel!
31935                 cfg.el = this.el.createChild();
31936                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31937                 this.add(region, ret);
31938                 break;
31939             */
31940             
31941             case 'Nest': 
31942                 // create a new Layout (which is  a Border Layout...
31943                 
31944                 var clayout = cfg.layout;
31945                 clayout.el  = this.el.createChild();
31946                 clayout.items   = clayout.items  || [];
31947                 
31948                 delete cfg.layout;
31949                 
31950                 // replace this exitems with the clayout ones..
31951                 xitems = clayout.items;
31952                  
31953                 // force background off if it's in center...
31954                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31955                     cfg.background = false;
31956                 }
31957                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31958                 
31959                 
31960                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31961                 //console.log('adding nested layout panel '  + cfg.toSource());
31962                 this.add(region, ret);
31963                 nb = {}; /// find first...
31964                 break;
31965             
31966             case 'Grid':
31967                 
31968                 // needs grid and region
31969                 
31970                 //var el = this.getRegion(region).el.createChild();
31971                 /*
31972                  *var el = this.el.createChild();
31973                 // create the grid first...
31974                 cfg.grid.container = el;
31975                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31976                 */
31977                 
31978                 if (region == 'center' && this.active ) {
31979                     cfg.background = false;
31980                 }
31981                 
31982                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31983                 
31984                 this.add(region, ret);
31985                 /*
31986                 if (cfg.background) {
31987                     // render grid on panel activation (if panel background)
31988                     ret.on('activate', function(gp) {
31989                         if (!gp.grid.rendered) {
31990                     //        gp.grid.render(el);
31991                         }
31992                     });
31993                 } else {
31994                   //  cfg.grid.render(el);
31995                 }
31996                 */
31997                 break;
31998            
31999            
32000             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32001                 // it was the old xcomponent building that caused this before.
32002                 // espeically if border is the top element in the tree.
32003                 ret = this;
32004                 break; 
32005                 
32006                     
32007                 
32008                 
32009                 
32010             default:
32011                 /*
32012                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32013                     
32014                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32015                     this.add(region, ret);
32016                 } else {
32017                 */
32018                     Roo.log(cfg);
32019                     throw "Can not add '" + cfg.xtype + "' to Border";
32020                     return null;
32021              
32022                                 
32023              
32024         }
32025         this.beginUpdate();
32026         // add children..
32027         var region = '';
32028         var abn = {};
32029         Roo.each(xitems, function(i)  {
32030             region = nb && i.region ? i.region : false;
32031             
32032             var add = ret.addxtype(i);
32033            
32034             if (region) {
32035                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32036                 if (!i.background) {
32037                     abn[region] = nb[region] ;
32038                 }
32039             }
32040             
32041         });
32042         this.endUpdate();
32043
32044         // make the last non-background panel active..
32045         //if (nb) { Roo.log(abn); }
32046         if (nb) {
32047             
32048             for(var r in abn) {
32049                 region = this.getRegion(r);
32050                 if (region) {
32051                     // tried using nb[r], but it does not work..
32052                      
32053                     region.showPanel(abn[r]);
32054                    
32055                 }
32056             }
32057         }
32058         return ret;
32059         
32060     },
32061     
32062     
32063 // private
32064     factory : function(cfg)
32065     {
32066         
32067         var validRegions = Roo.bootstrap.layout.Border.regions;
32068
32069         var target = cfg.region;
32070         cfg.mgr = this;
32071         
32072         var r = Roo.bootstrap.layout;
32073         Roo.log(target);
32074         switch(target){
32075             case "north":
32076                 return new r.North(cfg);
32077             case "south":
32078                 return new r.South(cfg);
32079             case "east":
32080                 return new r.East(cfg);
32081             case "west":
32082                 return new r.West(cfg);
32083             case "center":
32084                 return new r.Center(cfg);
32085         }
32086         throw 'Layout region "'+target+'" not supported.';
32087     }
32088     
32089     
32090 });
32091  /*
32092  * Based on:
32093  * Ext JS Library 1.1.1
32094  * Copyright(c) 2006-2007, Ext JS, LLC.
32095  *
32096  * Originally Released Under LGPL - original licence link has changed is not relivant.
32097  *
32098  * Fork - LGPL
32099  * <script type="text/javascript">
32100  */
32101  
32102 /**
32103  * @class Roo.bootstrap.layout.Basic
32104  * @extends Roo.util.Observable
32105  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32106  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32107  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32108  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32109  * @cfg {string}   region  the region that it inhabits..
32110  * @cfg {bool}   skipConfig skip config?
32111  * 
32112
32113  */
32114 Roo.bootstrap.layout.Basic = function(config){
32115     
32116     this.mgr = config.mgr;
32117     
32118     this.position = config.region;
32119     
32120     var skipConfig = config.skipConfig;
32121     
32122     this.events = {
32123         /**
32124          * @scope Roo.BasicLayoutRegion
32125          */
32126         
32127         /**
32128          * @event beforeremove
32129          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32130          * @param {Roo.LayoutRegion} this
32131          * @param {Roo.ContentPanel} panel The panel
32132          * @param {Object} e The cancel event object
32133          */
32134         "beforeremove" : true,
32135         /**
32136          * @event invalidated
32137          * Fires when the layout for this region is changed.
32138          * @param {Roo.LayoutRegion} this
32139          */
32140         "invalidated" : true,
32141         /**
32142          * @event visibilitychange
32143          * Fires when this region is shown or hidden 
32144          * @param {Roo.LayoutRegion} this
32145          * @param {Boolean} visibility true or false
32146          */
32147         "visibilitychange" : true,
32148         /**
32149          * @event paneladded
32150          * Fires when a panel is added. 
32151          * @param {Roo.LayoutRegion} this
32152          * @param {Roo.ContentPanel} panel The panel
32153          */
32154         "paneladded" : true,
32155         /**
32156          * @event panelremoved
32157          * Fires when a panel is removed. 
32158          * @param {Roo.LayoutRegion} this
32159          * @param {Roo.ContentPanel} panel The panel
32160          */
32161         "panelremoved" : true,
32162         /**
32163          * @event beforecollapse
32164          * Fires when this region before collapse.
32165          * @param {Roo.LayoutRegion} this
32166          */
32167         "beforecollapse" : true,
32168         /**
32169          * @event collapsed
32170          * Fires when this region is collapsed.
32171          * @param {Roo.LayoutRegion} this
32172          */
32173         "collapsed" : true,
32174         /**
32175          * @event expanded
32176          * Fires when this region is expanded.
32177          * @param {Roo.LayoutRegion} this
32178          */
32179         "expanded" : true,
32180         /**
32181          * @event slideshow
32182          * Fires when this region is slid into view.
32183          * @param {Roo.LayoutRegion} this
32184          */
32185         "slideshow" : true,
32186         /**
32187          * @event slidehide
32188          * Fires when this region slides out of view. 
32189          * @param {Roo.LayoutRegion} this
32190          */
32191         "slidehide" : true,
32192         /**
32193          * @event panelactivated
32194          * Fires when a panel is activated. 
32195          * @param {Roo.LayoutRegion} this
32196          * @param {Roo.ContentPanel} panel The activated panel
32197          */
32198         "panelactivated" : true,
32199         /**
32200          * @event resized
32201          * Fires when the user resizes this region. 
32202          * @param {Roo.LayoutRegion} this
32203          * @param {Number} newSize The new size (width for east/west, height for north/south)
32204          */
32205         "resized" : true
32206     };
32207     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32208     this.panels = new Roo.util.MixedCollection();
32209     this.panels.getKey = this.getPanelId.createDelegate(this);
32210     this.box = null;
32211     this.activePanel = null;
32212     // ensure listeners are added...
32213     
32214     if (config.listeners || config.events) {
32215         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32216             listeners : config.listeners || {},
32217             events : config.events || {}
32218         });
32219     }
32220     
32221     if(skipConfig !== true){
32222         this.applyConfig(config);
32223     }
32224 };
32225
32226 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32227 {
32228     getPanelId : function(p){
32229         return p.getId();
32230     },
32231     
32232     applyConfig : function(config){
32233         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32234         this.config = config;
32235         
32236     },
32237     
32238     /**
32239      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32240      * the width, for horizontal (north, south) the height.
32241      * @param {Number} newSize The new width or height
32242      */
32243     resizeTo : function(newSize){
32244         var el = this.el ? this.el :
32245                  (this.activePanel ? this.activePanel.getEl() : null);
32246         if(el){
32247             switch(this.position){
32248                 case "east":
32249                 case "west":
32250                     el.setWidth(newSize);
32251                     this.fireEvent("resized", this, newSize);
32252                 break;
32253                 case "north":
32254                 case "south":
32255                     el.setHeight(newSize);
32256                     this.fireEvent("resized", this, newSize);
32257                 break;                
32258             }
32259         }
32260     },
32261     
32262     getBox : function(){
32263         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32264     },
32265     
32266     getMargins : function(){
32267         return this.margins;
32268     },
32269     
32270     updateBox : function(box){
32271         this.box = box;
32272         var el = this.activePanel.getEl();
32273         el.dom.style.left = box.x + "px";
32274         el.dom.style.top = box.y + "px";
32275         this.activePanel.setSize(box.width, box.height);
32276     },
32277     
32278     /**
32279      * Returns the container element for this region.
32280      * @return {Roo.Element}
32281      */
32282     getEl : function(){
32283         return this.activePanel;
32284     },
32285     
32286     /**
32287      * Returns true if this region is currently visible.
32288      * @return {Boolean}
32289      */
32290     isVisible : function(){
32291         return this.activePanel ? true : false;
32292     },
32293     
32294     setActivePanel : function(panel){
32295         panel = this.getPanel(panel);
32296         if(this.activePanel && this.activePanel != panel){
32297             this.activePanel.setActiveState(false);
32298             this.activePanel.getEl().setLeftTop(-10000,-10000);
32299         }
32300         this.activePanel = panel;
32301         panel.setActiveState(true);
32302         if(this.box){
32303             panel.setSize(this.box.width, this.box.height);
32304         }
32305         this.fireEvent("panelactivated", this, panel);
32306         this.fireEvent("invalidated");
32307     },
32308     
32309     /**
32310      * Show the specified panel.
32311      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32312      * @return {Roo.ContentPanel} The shown panel or null
32313      */
32314     showPanel : function(panel){
32315         panel = this.getPanel(panel);
32316         if(panel){
32317             this.setActivePanel(panel);
32318         }
32319         return panel;
32320     },
32321     
32322     /**
32323      * Get the active panel for this region.
32324      * @return {Roo.ContentPanel} The active panel or null
32325      */
32326     getActivePanel : function(){
32327         return this.activePanel;
32328     },
32329     
32330     /**
32331      * Add the passed ContentPanel(s)
32332      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32333      * @return {Roo.ContentPanel} The panel added (if only one was added)
32334      */
32335     add : function(panel){
32336         if(arguments.length > 1){
32337             for(var i = 0, len = arguments.length; i < len; i++) {
32338                 this.add(arguments[i]);
32339             }
32340             return null;
32341         }
32342         if(this.hasPanel(panel)){
32343             this.showPanel(panel);
32344             return panel;
32345         }
32346         var el = panel.getEl();
32347         if(el.dom.parentNode != this.mgr.el.dom){
32348             this.mgr.el.dom.appendChild(el.dom);
32349         }
32350         if(panel.setRegion){
32351             panel.setRegion(this);
32352         }
32353         this.panels.add(panel);
32354         el.setStyle("position", "absolute");
32355         if(!panel.background){
32356             this.setActivePanel(panel);
32357             if(this.config.initialSize && this.panels.getCount()==1){
32358                 this.resizeTo(this.config.initialSize);
32359             }
32360         }
32361         this.fireEvent("paneladded", this, panel);
32362         return panel;
32363     },
32364     
32365     /**
32366      * Returns true if the panel is in this region.
32367      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32368      * @return {Boolean}
32369      */
32370     hasPanel : function(panel){
32371         if(typeof panel == "object"){ // must be panel obj
32372             panel = panel.getId();
32373         }
32374         return this.getPanel(panel) ? true : false;
32375     },
32376     
32377     /**
32378      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32379      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32380      * @param {Boolean} preservePanel Overrides the config preservePanel option
32381      * @return {Roo.ContentPanel} The panel that was removed
32382      */
32383     remove : function(panel, preservePanel){
32384         panel = this.getPanel(panel);
32385         if(!panel){
32386             return null;
32387         }
32388         var e = {};
32389         this.fireEvent("beforeremove", this, panel, e);
32390         if(e.cancel === true){
32391             return null;
32392         }
32393         var panelId = panel.getId();
32394         this.panels.removeKey(panelId);
32395         return panel;
32396     },
32397     
32398     /**
32399      * Returns the panel specified or null if it's not in this region.
32400      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32401      * @return {Roo.ContentPanel}
32402      */
32403     getPanel : function(id){
32404         if(typeof id == "object"){ // must be panel obj
32405             return id;
32406         }
32407         return this.panels.get(id);
32408     },
32409     
32410     /**
32411      * Returns this regions position (north/south/east/west/center).
32412      * @return {String} 
32413      */
32414     getPosition: function(){
32415         return this.position;    
32416     }
32417 });/*
32418  * Based on:
32419  * Ext JS Library 1.1.1
32420  * Copyright(c) 2006-2007, Ext JS, LLC.
32421  *
32422  * Originally Released Under LGPL - original licence link has changed is not relivant.
32423  *
32424  * Fork - LGPL
32425  * <script type="text/javascript">
32426  */
32427  
32428 /**
32429  * @class Roo.bootstrap.layout.Region
32430  * @extends Roo.bootstrap.layout.Basic
32431  * This class represents a region in a layout manager.
32432  
32433  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32434  * @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})
32435  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32436  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32437  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32438  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32439  * @cfg {String}    title           The title for the region (overrides panel titles)
32440  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32441  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32442  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32443  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32444  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32445  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32446  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32447  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32448  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32449  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32450
32451  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32452  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32453  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32454  * @cfg {Number}    width           For East/West panels
32455  * @cfg {Number}    height          For North/South panels
32456  * @cfg {Boolean}   split           To show the splitter
32457  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32458  * 
32459  * @cfg {string}   cls             Extra CSS classes to add to region
32460  * 
32461  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32462  * @cfg {string}   region  the region that it inhabits..
32463  *
32464
32465  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32466  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32467
32468  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32469  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32470  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32471  */
32472 Roo.bootstrap.layout.Region = function(config)
32473 {
32474     this.applyConfig(config);
32475
32476     var mgr = config.mgr;
32477     var pos = config.region;
32478     config.skipConfig = true;
32479     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32480     
32481     if (mgr.el) {
32482         this.onRender(mgr.el);   
32483     }
32484      
32485     this.visible = true;
32486     this.collapsed = false;
32487 };
32488
32489 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32490
32491     position: '', // set by wrapper (eg. north/south etc..)
32492
32493     createBody : function(){
32494         /** This region's body element 
32495         * @type Roo.Element */
32496         this.bodyEl = this.el.createChild({
32497                 tag: "div",
32498                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32499         });
32500     },
32501
32502     onRender: function(ctr, pos)
32503     {
32504         var dh = Roo.DomHelper;
32505         /** This region's container element 
32506         * @type Roo.Element */
32507         this.el = dh.append(ctr.dom, {
32508                 tag: "div",
32509                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32510             }, true);
32511         /** This region's title element 
32512         * @type Roo.Element */
32513     
32514         this.titleEl = dh.append(this.el.dom,
32515             {
32516                     tag: "div",
32517                     unselectable: "on",
32518                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32519                     children:[
32520                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32521                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32522                     ]}, true);
32523         
32524         this.titleEl.enableDisplayMode();
32525         /** This region's title text element 
32526         * @type HTMLElement */
32527         this.titleTextEl = this.titleEl.dom.firstChild;
32528         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32529         /*
32530         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32531         this.closeBtn.enableDisplayMode();
32532         this.closeBtn.on("click", this.closeClicked, this);
32533         this.closeBtn.hide();
32534     */
32535         this.createBody(this.config);
32536         if(this.config.hideWhenEmpty){
32537             this.hide();
32538             this.on("paneladded", this.validateVisibility, this);
32539             this.on("panelremoved", this.validateVisibility, this);
32540         }
32541         if(this.autoScroll){
32542             this.bodyEl.setStyle("overflow", "auto");
32543         }else{
32544             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32545         }
32546         //if(c.titlebar !== false){
32547             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32548                 this.titleEl.hide();
32549             }else{
32550                 this.titleEl.show();
32551                 if(this.config.title){
32552                     this.titleTextEl.innerHTML = this.config.title;
32553                 }
32554             }
32555         //}
32556         if(this.config.collapsed){
32557             this.collapse(true);
32558         }
32559         if(this.config.hidden){
32560             this.hide();
32561         }
32562     },
32563     
32564     applyConfig : function(c)
32565     {
32566         /*
32567          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32568             var dh = Roo.DomHelper;
32569             if(c.titlebar !== false){
32570                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32571                 this.collapseBtn.on("click", this.collapse, this);
32572                 this.collapseBtn.enableDisplayMode();
32573                 /*
32574                 if(c.showPin === true || this.showPin){
32575                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32576                     this.stickBtn.enableDisplayMode();
32577                     this.stickBtn.on("click", this.expand, this);
32578                     this.stickBtn.hide();
32579                 }
32580                 
32581             }
32582             */
32583             /** This region's collapsed element
32584             * @type Roo.Element */
32585             /*
32586              *
32587             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32588                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32589             ]}, true);
32590             
32591             if(c.floatable !== false){
32592                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32593                this.collapsedEl.on("click", this.collapseClick, this);
32594             }
32595
32596             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32597                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32598                    id: "message", unselectable: "on", style:{"float":"left"}});
32599                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32600              }
32601             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32602             this.expandBtn.on("click", this.expand, this);
32603             
32604         }
32605         
32606         if(this.collapseBtn){
32607             this.collapseBtn.setVisible(c.collapsible == true);
32608         }
32609         
32610         this.cmargins = c.cmargins || this.cmargins ||
32611                          (this.position == "west" || this.position == "east" ?
32612                              {top: 0, left: 2, right:2, bottom: 0} :
32613                              {top: 2, left: 0, right:0, bottom: 2});
32614         */
32615         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32616         
32617         
32618         this.bottomTabs = c.tabPosition != "top";
32619         
32620         this.autoScroll = c.autoScroll || false;
32621         
32622         
32623        
32624         
32625         this.duration = c.duration || .30;
32626         this.slideDuration = c.slideDuration || .45;
32627         this.config = c;
32628        
32629     },
32630     /**
32631      * Returns true if this region is currently visible.
32632      * @return {Boolean}
32633      */
32634     isVisible : function(){
32635         return this.visible;
32636     },
32637
32638     /**
32639      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32640      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32641      */
32642     //setCollapsedTitle : function(title){
32643     //    title = title || "&#160;";
32644      //   if(this.collapsedTitleTextEl){
32645       //      this.collapsedTitleTextEl.innerHTML = title;
32646        // }
32647     //},
32648
32649     getBox : function(){
32650         var b;
32651       //  if(!this.collapsed){
32652             b = this.el.getBox(false, true);
32653        // }else{
32654           //  b = this.collapsedEl.getBox(false, true);
32655         //}
32656         return b;
32657     },
32658
32659     getMargins : function(){
32660         return this.margins;
32661         //return this.collapsed ? this.cmargins : this.margins;
32662     },
32663 /*
32664     highlight : function(){
32665         this.el.addClass("x-layout-panel-dragover");
32666     },
32667
32668     unhighlight : function(){
32669         this.el.removeClass("x-layout-panel-dragover");
32670     },
32671 */
32672     updateBox : function(box)
32673     {
32674         this.box = box;
32675         if(!this.collapsed){
32676             this.el.dom.style.left = box.x + "px";
32677             this.el.dom.style.top = box.y + "px";
32678             this.updateBody(box.width, box.height);
32679         }else{
32680             this.collapsedEl.dom.style.left = box.x + "px";
32681             this.collapsedEl.dom.style.top = box.y + "px";
32682             this.collapsedEl.setSize(box.width, box.height);
32683         }
32684         if(this.tabs){
32685             this.tabs.autoSizeTabs();
32686         }
32687     },
32688
32689     updateBody : function(w, h)
32690     {
32691         if(w !== null){
32692             this.el.setWidth(w);
32693             w -= this.el.getBorderWidth("rl");
32694             if(this.config.adjustments){
32695                 w += this.config.adjustments[0];
32696             }
32697         }
32698         if(h !== null){
32699             this.el.setHeight(h);
32700             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32701             h -= this.el.getBorderWidth("tb");
32702             if(this.config.adjustments){
32703                 h += this.config.adjustments[1];
32704             }
32705             this.bodyEl.setHeight(h);
32706             if(this.tabs){
32707                 h = this.tabs.syncHeight(h);
32708             }
32709         }
32710         if(this.panelSize){
32711             w = w !== null ? w : this.panelSize.width;
32712             h = h !== null ? h : this.panelSize.height;
32713         }
32714         if(this.activePanel){
32715             var el = this.activePanel.getEl();
32716             w = w !== null ? w : el.getWidth();
32717             h = h !== null ? h : el.getHeight();
32718             this.panelSize = {width: w, height: h};
32719             this.activePanel.setSize(w, h);
32720         }
32721         if(Roo.isIE && this.tabs){
32722             this.tabs.el.repaint();
32723         }
32724     },
32725
32726     /**
32727      * Returns the container element for this region.
32728      * @return {Roo.Element}
32729      */
32730     getEl : function(){
32731         return this.el;
32732     },
32733
32734     /**
32735      * Hides this region.
32736      */
32737     hide : function(){
32738         //if(!this.collapsed){
32739             this.el.dom.style.left = "-2000px";
32740             this.el.hide();
32741         //}else{
32742          //   this.collapsedEl.dom.style.left = "-2000px";
32743          //   this.collapsedEl.hide();
32744        // }
32745         this.visible = false;
32746         this.fireEvent("visibilitychange", this, false);
32747     },
32748
32749     /**
32750      * Shows this region if it was previously hidden.
32751      */
32752     show : function(){
32753         //if(!this.collapsed){
32754             this.el.show();
32755         //}else{
32756         //    this.collapsedEl.show();
32757        // }
32758         this.visible = true;
32759         this.fireEvent("visibilitychange", this, true);
32760     },
32761 /*
32762     closeClicked : function(){
32763         if(this.activePanel){
32764             this.remove(this.activePanel);
32765         }
32766     },
32767
32768     collapseClick : function(e){
32769         if(this.isSlid){
32770            e.stopPropagation();
32771            this.slideIn();
32772         }else{
32773            e.stopPropagation();
32774            this.slideOut();
32775         }
32776     },
32777 */
32778     /**
32779      * Collapses this region.
32780      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32781      */
32782     /*
32783     collapse : function(skipAnim, skipCheck = false){
32784         if(this.collapsed) {
32785             return;
32786         }
32787         
32788         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32789             
32790             this.collapsed = true;
32791             if(this.split){
32792                 this.split.el.hide();
32793             }
32794             if(this.config.animate && skipAnim !== true){
32795                 this.fireEvent("invalidated", this);
32796                 this.animateCollapse();
32797             }else{
32798                 this.el.setLocation(-20000,-20000);
32799                 this.el.hide();
32800                 this.collapsedEl.show();
32801                 this.fireEvent("collapsed", this);
32802                 this.fireEvent("invalidated", this);
32803             }
32804         }
32805         
32806     },
32807 */
32808     animateCollapse : function(){
32809         // overridden
32810     },
32811
32812     /**
32813      * Expands this region if it was previously collapsed.
32814      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32815      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32816      */
32817     /*
32818     expand : function(e, skipAnim){
32819         if(e) {
32820             e.stopPropagation();
32821         }
32822         if(!this.collapsed || this.el.hasActiveFx()) {
32823             return;
32824         }
32825         if(this.isSlid){
32826             this.afterSlideIn();
32827             skipAnim = true;
32828         }
32829         this.collapsed = false;
32830         if(this.config.animate && skipAnim !== true){
32831             this.animateExpand();
32832         }else{
32833             this.el.show();
32834             if(this.split){
32835                 this.split.el.show();
32836             }
32837             this.collapsedEl.setLocation(-2000,-2000);
32838             this.collapsedEl.hide();
32839             this.fireEvent("invalidated", this);
32840             this.fireEvent("expanded", this);
32841         }
32842     },
32843 */
32844     animateExpand : function(){
32845         // overridden
32846     },
32847
32848     initTabs : function()
32849     {
32850         this.bodyEl.setStyle("overflow", "hidden");
32851         var ts = new Roo.bootstrap.panel.Tabs({
32852                 el: this.bodyEl.dom,
32853                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32854                 disableTooltips: this.config.disableTabTips,
32855                 toolbar : this.config.toolbar
32856             });
32857         
32858         if(this.config.hideTabs){
32859             ts.stripWrap.setDisplayed(false);
32860         }
32861         this.tabs = ts;
32862         ts.resizeTabs = this.config.resizeTabs === true;
32863         ts.minTabWidth = this.config.minTabWidth || 40;
32864         ts.maxTabWidth = this.config.maxTabWidth || 250;
32865         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32866         ts.monitorResize = false;
32867         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32868         ts.bodyEl.addClass('roo-layout-tabs-body');
32869         this.panels.each(this.initPanelAsTab, this);
32870     },
32871
32872     initPanelAsTab : function(panel){
32873         var ti = this.tabs.addTab(
32874                     panel.getEl().id,
32875                     panel.getTitle(), null,
32876                     this.config.closeOnTab && panel.isClosable()
32877             );
32878         if(panel.tabTip !== undefined){
32879             ti.setTooltip(panel.tabTip);
32880         }
32881         ti.on("activate", function(){
32882               this.setActivePanel(panel);
32883         }, this);
32884         
32885         if(this.config.closeOnTab){
32886             ti.on("beforeclose", function(t, e){
32887                 e.cancel = true;
32888                 this.remove(panel);
32889             }, this);
32890         }
32891         return ti;
32892     },
32893
32894     updatePanelTitle : function(panel, title)
32895     {
32896         if(this.activePanel == panel){
32897             this.updateTitle(title);
32898         }
32899         if(this.tabs){
32900             var ti = this.tabs.getTab(panel.getEl().id);
32901             ti.setText(title);
32902             if(panel.tabTip !== undefined){
32903                 ti.setTooltip(panel.tabTip);
32904             }
32905         }
32906     },
32907
32908     updateTitle : function(title){
32909         if(this.titleTextEl && !this.config.title){
32910             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32911         }
32912     },
32913
32914     setActivePanel : function(panel)
32915     {
32916         panel = this.getPanel(panel);
32917         if(this.activePanel && this.activePanel != panel){
32918             this.activePanel.setActiveState(false);
32919         }
32920         this.activePanel = panel;
32921         panel.setActiveState(true);
32922         if(this.panelSize){
32923             panel.setSize(this.panelSize.width, this.panelSize.height);
32924         }
32925         if(this.closeBtn){
32926             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32927         }
32928         this.updateTitle(panel.getTitle());
32929         if(this.tabs){
32930             this.fireEvent("invalidated", this);
32931         }
32932         this.fireEvent("panelactivated", this, panel);
32933     },
32934
32935     /**
32936      * Shows the specified panel.
32937      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32938      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32939      */
32940     showPanel : function(panel)
32941     {
32942         panel = this.getPanel(panel);
32943         if(panel){
32944             if(this.tabs){
32945                 var tab = this.tabs.getTab(panel.getEl().id);
32946                 if(tab.isHidden()){
32947                     this.tabs.unhideTab(tab.id);
32948                 }
32949                 tab.activate();
32950             }else{
32951                 this.setActivePanel(panel);
32952             }
32953         }
32954         return panel;
32955     },
32956
32957     /**
32958      * Get the active panel for this region.
32959      * @return {Roo.ContentPanel} The active panel or null
32960      */
32961     getActivePanel : function(){
32962         return this.activePanel;
32963     },
32964
32965     validateVisibility : function(){
32966         if(this.panels.getCount() < 1){
32967             this.updateTitle("&#160;");
32968             this.closeBtn.hide();
32969             this.hide();
32970         }else{
32971             if(!this.isVisible()){
32972                 this.show();
32973             }
32974         }
32975     },
32976
32977     /**
32978      * Adds the passed ContentPanel(s) to this region.
32979      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32980      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32981      */
32982     add : function(panel){
32983         if(arguments.length > 1){
32984             for(var i = 0, len = arguments.length; i < len; i++) {
32985                 this.add(arguments[i]);
32986             }
32987             return null;
32988         }
32989         if(this.hasPanel(panel)){
32990             this.showPanel(panel);
32991             return panel;
32992         }
32993         panel.setRegion(this);
32994         this.panels.add(panel);
32995         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32996             this.bodyEl.dom.appendChild(panel.getEl().dom);
32997             if(panel.background !== true){
32998                 this.setActivePanel(panel);
32999             }
33000             this.fireEvent("paneladded", this, panel);
33001             return panel;
33002         }
33003         if(!this.tabs){
33004             this.initTabs();
33005         }else{
33006             this.initPanelAsTab(panel);
33007         }
33008         
33009         
33010         if(panel.background !== true){
33011             this.tabs.activate(panel.getEl().id);
33012         }
33013         this.fireEvent("paneladded", this, panel);
33014         return panel;
33015     },
33016
33017     /**
33018      * Hides the tab for the specified panel.
33019      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33020      */
33021     hidePanel : function(panel){
33022         if(this.tabs && (panel = this.getPanel(panel))){
33023             this.tabs.hideTab(panel.getEl().id);
33024         }
33025     },
33026
33027     /**
33028      * Unhides the tab for a previously hidden panel.
33029      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33030      */
33031     unhidePanel : function(panel){
33032         if(this.tabs && (panel = this.getPanel(panel))){
33033             this.tabs.unhideTab(panel.getEl().id);
33034         }
33035     },
33036
33037     clearPanels : function(){
33038         while(this.panels.getCount() > 0){
33039              this.remove(this.panels.first());
33040         }
33041     },
33042
33043     /**
33044      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33045      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33046      * @param {Boolean} preservePanel Overrides the config preservePanel option
33047      * @return {Roo.ContentPanel} The panel that was removed
33048      */
33049     remove : function(panel, preservePanel)
33050     {
33051         panel = this.getPanel(panel);
33052         if(!panel){
33053             return null;
33054         }
33055         var e = {};
33056         this.fireEvent("beforeremove", this, panel, e);
33057         if(e.cancel === true){
33058             return null;
33059         }
33060         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33061         var panelId = panel.getId();
33062         this.panels.removeKey(panelId);
33063         if(preservePanel){
33064             document.body.appendChild(panel.getEl().dom);
33065         }
33066         if(this.tabs){
33067             this.tabs.removeTab(panel.getEl().id);
33068         }else if (!preservePanel){
33069             this.bodyEl.dom.removeChild(panel.getEl().dom);
33070         }
33071         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33072             var p = this.panels.first();
33073             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33074             tempEl.appendChild(p.getEl().dom);
33075             this.bodyEl.update("");
33076             this.bodyEl.dom.appendChild(p.getEl().dom);
33077             tempEl = null;
33078             this.updateTitle(p.getTitle());
33079             this.tabs = null;
33080             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33081             this.setActivePanel(p);
33082         }
33083         panel.setRegion(null);
33084         if(this.activePanel == panel){
33085             this.activePanel = null;
33086         }
33087         if(this.config.autoDestroy !== false && preservePanel !== true){
33088             try{panel.destroy();}catch(e){}
33089         }
33090         this.fireEvent("panelremoved", this, panel);
33091         return panel;
33092     },
33093
33094     /**
33095      * Returns the TabPanel component used by this region
33096      * @return {Roo.TabPanel}
33097      */
33098     getTabs : function(){
33099         return this.tabs;
33100     },
33101
33102     createTool : function(parentEl, className){
33103         var btn = Roo.DomHelper.append(parentEl, {
33104             tag: "div",
33105             cls: "x-layout-tools-button",
33106             children: [ {
33107                 tag: "div",
33108                 cls: "roo-layout-tools-button-inner " + className,
33109                 html: "&#160;"
33110             }]
33111         }, true);
33112         btn.addClassOnOver("roo-layout-tools-button-over");
33113         return btn;
33114     }
33115 });/*
33116  * Based on:
33117  * Ext JS Library 1.1.1
33118  * Copyright(c) 2006-2007, Ext JS, LLC.
33119  *
33120  * Originally Released Under LGPL - original licence link has changed is not relivant.
33121  *
33122  * Fork - LGPL
33123  * <script type="text/javascript">
33124  */
33125  
33126
33127
33128 /**
33129  * @class Roo.SplitLayoutRegion
33130  * @extends Roo.LayoutRegion
33131  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33132  */
33133 Roo.bootstrap.layout.Split = function(config){
33134     this.cursor = config.cursor;
33135     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33136 };
33137
33138 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33139 {
33140     splitTip : "Drag to resize.",
33141     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33142     useSplitTips : false,
33143
33144     applyConfig : function(config){
33145         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33146     },
33147     
33148     onRender : function(ctr,pos) {
33149         
33150         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33151         if(!this.config.split){
33152             return;
33153         }
33154         if(!this.split){
33155             
33156             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33157                             tag: "div",
33158                             id: this.el.id + "-split",
33159                             cls: "roo-layout-split roo-layout-split-"+this.position,
33160                             html: "&#160;"
33161             });
33162             /** The SplitBar for this region 
33163             * @type Roo.SplitBar */
33164             // does not exist yet...
33165             Roo.log([this.position, this.orientation]);
33166             
33167             this.split = new Roo.bootstrap.SplitBar({
33168                 dragElement : splitEl,
33169                 resizingElement: this.el,
33170                 orientation : this.orientation
33171             });
33172             
33173             this.split.on("moved", this.onSplitMove, this);
33174             this.split.useShim = this.config.useShim === true;
33175             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33176             if(this.useSplitTips){
33177                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33178             }
33179             //if(config.collapsible){
33180             //    this.split.el.on("dblclick", this.collapse,  this);
33181             //}
33182         }
33183         if(typeof this.config.minSize != "undefined"){
33184             this.split.minSize = this.config.minSize;
33185         }
33186         if(typeof this.config.maxSize != "undefined"){
33187             this.split.maxSize = this.config.maxSize;
33188         }
33189         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33190             this.hideSplitter();
33191         }
33192         
33193     },
33194
33195     getHMaxSize : function(){
33196          var cmax = this.config.maxSize || 10000;
33197          var center = this.mgr.getRegion("center");
33198          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33199     },
33200
33201     getVMaxSize : function(){
33202          var cmax = this.config.maxSize || 10000;
33203          var center = this.mgr.getRegion("center");
33204          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33205     },
33206
33207     onSplitMove : function(split, newSize){
33208         this.fireEvent("resized", this, newSize);
33209     },
33210     
33211     /** 
33212      * Returns the {@link Roo.SplitBar} for this region.
33213      * @return {Roo.SplitBar}
33214      */
33215     getSplitBar : function(){
33216         return this.split;
33217     },
33218     
33219     hide : function(){
33220         this.hideSplitter();
33221         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33222     },
33223
33224     hideSplitter : function(){
33225         if(this.split){
33226             this.split.el.setLocation(-2000,-2000);
33227             this.split.el.hide();
33228         }
33229     },
33230
33231     show : function(){
33232         if(this.split){
33233             this.split.el.show();
33234         }
33235         Roo.bootstrap.layout.Split.superclass.show.call(this);
33236     },
33237     
33238     beforeSlide: function(){
33239         if(Roo.isGecko){// firefox overflow auto bug workaround
33240             this.bodyEl.clip();
33241             if(this.tabs) {
33242                 this.tabs.bodyEl.clip();
33243             }
33244             if(this.activePanel){
33245                 this.activePanel.getEl().clip();
33246                 
33247                 if(this.activePanel.beforeSlide){
33248                     this.activePanel.beforeSlide();
33249                 }
33250             }
33251         }
33252     },
33253     
33254     afterSlide : function(){
33255         if(Roo.isGecko){// firefox overflow auto bug workaround
33256             this.bodyEl.unclip();
33257             if(this.tabs) {
33258                 this.tabs.bodyEl.unclip();
33259             }
33260             if(this.activePanel){
33261                 this.activePanel.getEl().unclip();
33262                 if(this.activePanel.afterSlide){
33263                     this.activePanel.afterSlide();
33264                 }
33265             }
33266         }
33267     },
33268
33269     initAutoHide : function(){
33270         if(this.autoHide !== false){
33271             if(!this.autoHideHd){
33272                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33273                 this.autoHideHd = {
33274                     "mouseout": function(e){
33275                         if(!e.within(this.el, true)){
33276                             st.delay(500);
33277                         }
33278                     },
33279                     "mouseover" : function(e){
33280                         st.cancel();
33281                     },
33282                     scope : this
33283                 };
33284             }
33285             this.el.on(this.autoHideHd);
33286         }
33287     },
33288
33289     clearAutoHide : function(){
33290         if(this.autoHide !== false){
33291             this.el.un("mouseout", this.autoHideHd.mouseout);
33292             this.el.un("mouseover", this.autoHideHd.mouseover);
33293         }
33294     },
33295
33296     clearMonitor : function(){
33297         Roo.get(document).un("click", this.slideInIf, this);
33298     },
33299
33300     // these names are backwards but not changed for compat
33301     slideOut : function(){
33302         if(this.isSlid || this.el.hasActiveFx()){
33303             return;
33304         }
33305         this.isSlid = true;
33306         if(this.collapseBtn){
33307             this.collapseBtn.hide();
33308         }
33309         this.closeBtnState = this.closeBtn.getStyle('display');
33310         this.closeBtn.hide();
33311         if(this.stickBtn){
33312             this.stickBtn.show();
33313         }
33314         this.el.show();
33315         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33316         this.beforeSlide();
33317         this.el.setStyle("z-index", 10001);
33318         this.el.slideIn(this.getSlideAnchor(), {
33319             callback: function(){
33320                 this.afterSlide();
33321                 this.initAutoHide();
33322                 Roo.get(document).on("click", this.slideInIf, this);
33323                 this.fireEvent("slideshow", this);
33324             },
33325             scope: this,
33326             block: true
33327         });
33328     },
33329
33330     afterSlideIn : function(){
33331         this.clearAutoHide();
33332         this.isSlid = false;
33333         this.clearMonitor();
33334         this.el.setStyle("z-index", "");
33335         if(this.collapseBtn){
33336             this.collapseBtn.show();
33337         }
33338         this.closeBtn.setStyle('display', this.closeBtnState);
33339         if(this.stickBtn){
33340             this.stickBtn.hide();
33341         }
33342         this.fireEvent("slidehide", this);
33343     },
33344
33345     slideIn : function(cb){
33346         if(!this.isSlid || this.el.hasActiveFx()){
33347             Roo.callback(cb);
33348             return;
33349         }
33350         this.isSlid = false;
33351         this.beforeSlide();
33352         this.el.slideOut(this.getSlideAnchor(), {
33353             callback: function(){
33354                 this.el.setLeftTop(-10000, -10000);
33355                 this.afterSlide();
33356                 this.afterSlideIn();
33357                 Roo.callback(cb);
33358             },
33359             scope: this,
33360             block: true
33361         });
33362     },
33363     
33364     slideInIf : function(e){
33365         if(!e.within(this.el)){
33366             this.slideIn();
33367         }
33368     },
33369
33370     animateCollapse : function(){
33371         this.beforeSlide();
33372         this.el.setStyle("z-index", 20000);
33373         var anchor = this.getSlideAnchor();
33374         this.el.slideOut(anchor, {
33375             callback : function(){
33376                 this.el.setStyle("z-index", "");
33377                 this.collapsedEl.slideIn(anchor, {duration:.3});
33378                 this.afterSlide();
33379                 this.el.setLocation(-10000,-10000);
33380                 this.el.hide();
33381                 this.fireEvent("collapsed", this);
33382             },
33383             scope: this,
33384             block: true
33385         });
33386     },
33387
33388     animateExpand : function(){
33389         this.beforeSlide();
33390         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33391         this.el.setStyle("z-index", 20000);
33392         this.collapsedEl.hide({
33393             duration:.1
33394         });
33395         this.el.slideIn(this.getSlideAnchor(), {
33396             callback : function(){
33397                 this.el.setStyle("z-index", "");
33398                 this.afterSlide();
33399                 if(this.split){
33400                     this.split.el.show();
33401                 }
33402                 this.fireEvent("invalidated", this);
33403                 this.fireEvent("expanded", this);
33404             },
33405             scope: this,
33406             block: true
33407         });
33408     },
33409
33410     anchors : {
33411         "west" : "left",
33412         "east" : "right",
33413         "north" : "top",
33414         "south" : "bottom"
33415     },
33416
33417     sanchors : {
33418         "west" : "l",
33419         "east" : "r",
33420         "north" : "t",
33421         "south" : "b"
33422     },
33423
33424     canchors : {
33425         "west" : "tl-tr",
33426         "east" : "tr-tl",
33427         "north" : "tl-bl",
33428         "south" : "bl-tl"
33429     },
33430
33431     getAnchor : function(){
33432         return this.anchors[this.position];
33433     },
33434
33435     getCollapseAnchor : function(){
33436         return this.canchors[this.position];
33437     },
33438
33439     getSlideAnchor : function(){
33440         return this.sanchors[this.position];
33441     },
33442
33443     getAlignAdj : function(){
33444         var cm = this.cmargins;
33445         switch(this.position){
33446             case "west":
33447                 return [0, 0];
33448             break;
33449             case "east":
33450                 return [0, 0];
33451             break;
33452             case "north":
33453                 return [0, 0];
33454             break;
33455             case "south":
33456                 return [0, 0];
33457             break;
33458         }
33459     },
33460
33461     getExpandAdj : function(){
33462         var c = this.collapsedEl, cm = this.cmargins;
33463         switch(this.position){
33464             case "west":
33465                 return [-(cm.right+c.getWidth()+cm.left), 0];
33466             break;
33467             case "east":
33468                 return [cm.right+c.getWidth()+cm.left, 0];
33469             break;
33470             case "north":
33471                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33472             break;
33473             case "south":
33474                 return [0, cm.top+cm.bottom+c.getHeight()];
33475             break;
33476         }
33477     }
33478 });/*
33479  * Based on:
33480  * Ext JS Library 1.1.1
33481  * Copyright(c) 2006-2007, Ext JS, LLC.
33482  *
33483  * Originally Released Under LGPL - original licence link has changed is not relivant.
33484  *
33485  * Fork - LGPL
33486  * <script type="text/javascript">
33487  */
33488 /*
33489  * These classes are private internal classes
33490  */
33491 Roo.bootstrap.layout.Center = function(config){
33492     config.region = "center";
33493     Roo.bootstrap.layout.Region.call(this, config);
33494     this.visible = true;
33495     this.minWidth = config.minWidth || 20;
33496     this.minHeight = config.minHeight || 20;
33497 };
33498
33499 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33500     hide : function(){
33501         // center panel can't be hidden
33502     },
33503     
33504     show : function(){
33505         // center panel can't be hidden
33506     },
33507     
33508     getMinWidth: function(){
33509         return this.minWidth;
33510     },
33511     
33512     getMinHeight: function(){
33513         return this.minHeight;
33514     }
33515 });
33516
33517
33518
33519
33520  
33521
33522
33523
33524
33525
33526 Roo.bootstrap.layout.North = function(config)
33527 {
33528     config.region = 'north';
33529     config.cursor = 'n-resize';
33530     
33531     Roo.bootstrap.layout.Split.call(this, config);
33532     
33533     
33534     if(this.split){
33535         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33536         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33537         this.split.el.addClass("roo-layout-split-v");
33538     }
33539     var size = config.initialSize || config.height;
33540     if(typeof size != "undefined"){
33541         this.el.setHeight(size);
33542     }
33543 };
33544 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33545 {
33546     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33547     
33548     
33549     
33550     getBox : function(){
33551         if(this.collapsed){
33552             return this.collapsedEl.getBox();
33553         }
33554         var box = this.el.getBox();
33555         if(this.split){
33556             box.height += this.split.el.getHeight();
33557         }
33558         return box;
33559     },
33560     
33561     updateBox : function(box){
33562         if(this.split && !this.collapsed){
33563             box.height -= this.split.el.getHeight();
33564             this.split.el.setLeft(box.x);
33565             this.split.el.setTop(box.y+box.height);
33566             this.split.el.setWidth(box.width);
33567         }
33568         if(this.collapsed){
33569             this.updateBody(box.width, null);
33570         }
33571         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33572     }
33573 });
33574
33575
33576
33577
33578
33579 Roo.bootstrap.layout.South = function(config){
33580     config.region = 'south';
33581     config.cursor = 's-resize';
33582     Roo.bootstrap.layout.Split.call(this, config);
33583     if(this.split){
33584         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33585         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33586         this.split.el.addClass("roo-layout-split-v");
33587     }
33588     var size = config.initialSize || config.height;
33589     if(typeof size != "undefined"){
33590         this.el.setHeight(size);
33591     }
33592 };
33593
33594 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33595     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33596     getBox : function(){
33597         if(this.collapsed){
33598             return this.collapsedEl.getBox();
33599         }
33600         var box = this.el.getBox();
33601         if(this.split){
33602             var sh = this.split.el.getHeight();
33603             box.height += sh;
33604             box.y -= sh;
33605         }
33606         return box;
33607     },
33608     
33609     updateBox : function(box){
33610         if(this.split && !this.collapsed){
33611             var sh = this.split.el.getHeight();
33612             box.height -= sh;
33613             box.y += sh;
33614             this.split.el.setLeft(box.x);
33615             this.split.el.setTop(box.y-sh);
33616             this.split.el.setWidth(box.width);
33617         }
33618         if(this.collapsed){
33619             this.updateBody(box.width, null);
33620         }
33621         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33622     }
33623 });
33624
33625 Roo.bootstrap.layout.East = function(config){
33626     config.region = "east";
33627     config.cursor = "e-resize";
33628     Roo.bootstrap.layout.Split.call(this, config);
33629     if(this.split){
33630         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33631         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33632         this.split.el.addClass("roo-layout-split-h");
33633     }
33634     var size = config.initialSize || config.width;
33635     if(typeof size != "undefined"){
33636         this.el.setWidth(size);
33637     }
33638 };
33639 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33640     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33641     getBox : function(){
33642         if(this.collapsed){
33643             return this.collapsedEl.getBox();
33644         }
33645         var box = this.el.getBox();
33646         if(this.split){
33647             var sw = this.split.el.getWidth();
33648             box.width += sw;
33649             box.x -= sw;
33650         }
33651         return box;
33652     },
33653
33654     updateBox : function(box){
33655         if(this.split && !this.collapsed){
33656             var sw = this.split.el.getWidth();
33657             box.width -= sw;
33658             this.split.el.setLeft(box.x);
33659             this.split.el.setTop(box.y);
33660             this.split.el.setHeight(box.height);
33661             box.x += sw;
33662         }
33663         if(this.collapsed){
33664             this.updateBody(null, box.height);
33665         }
33666         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33667     }
33668 });
33669
33670 Roo.bootstrap.layout.West = function(config){
33671     config.region = "west";
33672     config.cursor = "w-resize";
33673     
33674     Roo.bootstrap.layout.Split.call(this, config);
33675     if(this.split){
33676         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33677         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33678         this.split.el.addClass("roo-layout-split-h");
33679     }
33680     
33681 };
33682 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33683     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33684     
33685     onRender: function(ctr, pos)
33686     {
33687         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33688         var size = this.config.initialSize || this.config.width;
33689         if(typeof size != "undefined"){
33690             this.el.setWidth(size);
33691         }
33692     },
33693     
33694     getBox : function(){
33695         if(this.collapsed){
33696             return this.collapsedEl.getBox();
33697         }
33698         var box = this.el.getBox();
33699         if(this.split){
33700             box.width += this.split.el.getWidth();
33701         }
33702         return box;
33703     },
33704     
33705     updateBox : function(box){
33706         if(this.split && !this.collapsed){
33707             var sw = this.split.el.getWidth();
33708             box.width -= sw;
33709             this.split.el.setLeft(box.x+box.width);
33710             this.split.el.setTop(box.y);
33711             this.split.el.setHeight(box.height);
33712         }
33713         if(this.collapsed){
33714             this.updateBody(null, box.height);
33715         }
33716         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33717     }
33718 });
33719 Roo.namespace("Roo.bootstrap.panel");/*
33720  * Based on:
33721  * Ext JS Library 1.1.1
33722  * Copyright(c) 2006-2007, Ext JS, LLC.
33723  *
33724  * Originally Released Under LGPL - original licence link has changed is not relivant.
33725  *
33726  * Fork - LGPL
33727  * <script type="text/javascript">
33728  */
33729 /**
33730  * @class Roo.ContentPanel
33731  * @extends Roo.util.Observable
33732  * A basic ContentPanel element.
33733  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33734  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33735  * @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
33736  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33737  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33738  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33739  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33740  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33741  * @cfg {String} title          The title for this panel
33742  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33743  * @cfg {String} url            Calls {@link #setUrl} with this value
33744  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33745  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33746  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33747  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33748
33749  * @constructor
33750  * Create a new ContentPanel.
33751  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33752  * @param {String/Object} config A string to set only the title or a config object
33753  * @param {String} content (optional) Set the HTML content for this panel
33754  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33755  */
33756 Roo.bootstrap.panel.Content = function( config){
33757     
33758     var el = config.el;
33759     var content = config.content;
33760
33761     if(config.autoCreate){ // xtype is available if this is called from factory
33762         el = Roo.id();
33763     }
33764     this.el = Roo.get(el);
33765     if(!this.el && config && config.autoCreate){
33766         if(typeof config.autoCreate == "object"){
33767             if(!config.autoCreate.id){
33768                 config.autoCreate.id = config.id||el;
33769             }
33770             this.el = Roo.DomHelper.append(document.body,
33771                         config.autoCreate, true);
33772         }else{
33773             var elcfg =  {   tag: "div",
33774                             cls: "roo-layout-inactive-content",
33775                             id: config.id||el
33776                             };
33777             if (config.html) {
33778                 elcfg.html = config.html;
33779                 
33780             }
33781                         
33782             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33783         }
33784     } 
33785     this.closable = false;
33786     this.loaded = false;
33787     this.active = false;
33788    
33789       
33790     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33791         
33792         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33793         
33794         this.wrapEl = this.el.wrap();
33795         var ti = [];
33796         if (config.toolbar.items) {
33797             ti = config.toolbar.items ;
33798             delete config.toolbar.items ;
33799         }
33800         
33801         var nitems = [];
33802         this.toolbar.render(this.wrapEl, 'before');
33803         for(var i =0;i < ti.length;i++) {
33804           //  Roo.log(['add child', items[i]]);
33805             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33806         }
33807         this.toolbar.items = nitems;
33808         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33809         delete config.toolbar;
33810         
33811     }
33812     /*
33813     // xtype created footer. - not sure if will work as we normally have to render first..
33814     if (this.footer && !this.footer.el && this.footer.xtype) {
33815         if (!this.wrapEl) {
33816             this.wrapEl = this.el.wrap();
33817         }
33818     
33819         this.footer.container = this.wrapEl.createChild();
33820          
33821         this.footer = Roo.factory(this.footer, Roo);
33822         
33823     }
33824     */
33825     
33826      if(typeof config == "string"){
33827         this.title = config;
33828     }else{
33829         Roo.apply(this, config);
33830     }
33831     
33832     if(this.resizeEl){
33833         this.resizeEl = Roo.get(this.resizeEl, true);
33834     }else{
33835         this.resizeEl = this.el;
33836     }
33837     // handle view.xtype
33838     
33839  
33840     
33841     
33842     this.addEvents({
33843         /**
33844          * @event activate
33845          * Fires when this panel is activated. 
33846          * @param {Roo.ContentPanel} this
33847          */
33848         "activate" : true,
33849         /**
33850          * @event deactivate
33851          * Fires when this panel is activated. 
33852          * @param {Roo.ContentPanel} this
33853          */
33854         "deactivate" : true,
33855
33856         /**
33857          * @event resize
33858          * Fires when this panel is resized if fitToFrame is true.
33859          * @param {Roo.ContentPanel} this
33860          * @param {Number} width The width after any component adjustments
33861          * @param {Number} height The height after any component adjustments
33862          */
33863         "resize" : true,
33864         
33865          /**
33866          * @event render
33867          * Fires when this tab is created
33868          * @param {Roo.ContentPanel} this
33869          */
33870         "render" : true
33871         
33872         
33873         
33874     });
33875     
33876
33877     
33878     
33879     if(this.autoScroll){
33880         this.resizeEl.setStyle("overflow", "auto");
33881     } else {
33882         // fix randome scrolling
33883         //this.el.on('scroll', function() {
33884         //    Roo.log('fix random scolling');
33885         //    this.scrollTo('top',0); 
33886         //});
33887     }
33888     content = content || this.content;
33889     if(content){
33890         this.setContent(content);
33891     }
33892     if(config && config.url){
33893         this.setUrl(this.url, this.params, this.loadOnce);
33894     }
33895     
33896     
33897     
33898     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33899     
33900     if (this.view && typeof(this.view.xtype) != 'undefined') {
33901         this.view.el = this.el.appendChild(document.createElement("div"));
33902         this.view = Roo.factory(this.view); 
33903         this.view.render  &&  this.view.render(false, '');  
33904     }
33905     
33906     
33907     this.fireEvent('render', this);
33908 };
33909
33910 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33911     tabTip:'',
33912     setRegion : function(region){
33913         this.region = region;
33914         if(region){
33915            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33916         }else{
33917            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33918         } 
33919     },
33920     
33921     /**
33922      * Returns the toolbar for this Panel if one was configured. 
33923      * @return {Roo.Toolbar} 
33924      */
33925     getToolbar : function(){
33926         return this.toolbar;
33927     },
33928     
33929     setActiveState : function(active){
33930         this.active = active;
33931         if(!active){
33932             this.fireEvent("deactivate", this);
33933         }else{
33934             this.fireEvent("activate", this);
33935         }
33936     },
33937     /**
33938      * Updates this panel's element
33939      * @param {String} content The new content
33940      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33941     */
33942     setContent : function(content, loadScripts){
33943         this.el.update(content, loadScripts);
33944     },
33945
33946     ignoreResize : function(w, h){
33947         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33948             return true;
33949         }else{
33950             this.lastSize = {width: w, height: h};
33951             return false;
33952         }
33953     },
33954     /**
33955      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33956      * @return {Roo.UpdateManager} The UpdateManager
33957      */
33958     getUpdateManager : function(){
33959         return this.el.getUpdateManager();
33960     },
33961      /**
33962      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33963      * @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:
33964 <pre><code>
33965 panel.load({
33966     url: "your-url.php",
33967     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33968     callback: yourFunction,
33969     scope: yourObject, //(optional scope)
33970     discardUrl: false,
33971     nocache: false,
33972     text: "Loading...",
33973     timeout: 30,
33974     scripts: false
33975 });
33976 </code></pre>
33977      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33978      * 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.
33979      * @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}
33980      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33981      * @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.
33982      * @return {Roo.ContentPanel} this
33983      */
33984     load : function(){
33985         var um = this.el.getUpdateManager();
33986         um.update.apply(um, arguments);
33987         return this;
33988     },
33989
33990
33991     /**
33992      * 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.
33993      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33994      * @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)
33995      * @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)
33996      * @return {Roo.UpdateManager} The UpdateManager
33997      */
33998     setUrl : function(url, params, loadOnce){
33999         if(this.refreshDelegate){
34000             this.removeListener("activate", this.refreshDelegate);
34001         }
34002         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34003         this.on("activate", this.refreshDelegate);
34004         return this.el.getUpdateManager();
34005     },
34006     
34007     _handleRefresh : function(url, params, loadOnce){
34008         if(!loadOnce || !this.loaded){
34009             var updater = this.el.getUpdateManager();
34010             updater.update(url, params, this._setLoaded.createDelegate(this));
34011         }
34012     },
34013     
34014     _setLoaded : function(){
34015         this.loaded = true;
34016     }, 
34017     
34018     /**
34019      * Returns this panel's id
34020      * @return {String} 
34021      */
34022     getId : function(){
34023         return this.el.id;
34024     },
34025     
34026     /** 
34027      * Returns this panel's element - used by regiosn to add.
34028      * @return {Roo.Element} 
34029      */
34030     getEl : function(){
34031         return this.wrapEl || this.el;
34032     },
34033     
34034    
34035     
34036     adjustForComponents : function(width, height)
34037     {
34038         //Roo.log('adjustForComponents ');
34039         if(this.resizeEl != this.el){
34040             width -= this.el.getFrameWidth('lr');
34041             height -= this.el.getFrameWidth('tb');
34042         }
34043         if(this.toolbar){
34044             var te = this.toolbar.getEl();
34045             height -= te.getHeight();
34046             te.setWidth(width);
34047         }
34048         if(this.footer){
34049             var te = this.footer.getEl();
34050             Roo.log("footer:" + te.getHeight());
34051             
34052             height -= te.getHeight();
34053             te.setWidth(width);
34054         }
34055         
34056         
34057         if(this.adjustments){
34058             width += this.adjustments[0];
34059             height += this.adjustments[1];
34060         }
34061         return {"width": width, "height": height};
34062     },
34063     
34064     setSize : function(width, height){
34065         if(this.fitToFrame && !this.ignoreResize(width, height)){
34066             if(this.fitContainer && this.resizeEl != this.el){
34067                 this.el.setSize(width, height);
34068             }
34069             var size = this.adjustForComponents(width, height);
34070             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34071             this.fireEvent('resize', this, size.width, size.height);
34072         }
34073     },
34074     
34075     /**
34076      * Returns this panel's title
34077      * @return {String} 
34078      */
34079     getTitle : function(){
34080         return this.title;
34081     },
34082     
34083     /**
34084      * Set this panel's title
34085      * @param {String} title
34086      */
34087     setTitle : function(title){
34088         this.title = title;
34089         if(this.region){
34090             this.region.updatePanelTitle(this, title);
34091         }
34092     },
34093     
34094     /**
34095      * Returns true is this panel was configured to be closable
34096      * @return {Boolean} 
34097      */
34098     isClosable : function(){
34099         return this.closable;
34100     },
34101     
34102     beforeSlide : function(){
34103         this.el.clip();
34104         this.resizeEl.clip();
34105     },
34106     
34107     afterSlide : function(){
34108         this.el.unclip();
34109         this.resizeEl.unclip();
34110     },
34111     
34112     /**
34113      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34114      *   Will fail silently if the {@link #setUrl} method has not been called.
34115      *   This does not activate the panel, just updates its content.
34116      */
34117     refresh : function(){
34118         if(this.refreshDelegate){
34119            this.loaded = false;
34120            this.refreshDelegate();
34121         }
34122     },
34123     
34124     /**
34125      * Destroys this panel
34126      */
34127     destroy : function(){
34128         this.el.removeAllListeners();
34129         var tempEl = document.createElement("span");
34130         tempEl.appendChild(this.el.dom);
34131         tempEl.innerHTML = "";
34132         this.el.remove();
34133         this.el = null;
34134     },
34135     
34136     /**
34137      * form - if the content panel contains a form - this is a reference to it.
34138      * @type {Roo.form.Form}
34139      */
34140     form : false,
34141     /**
34142      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34143      *    This contains a reference to it.
34144      * @type {Roo.View}
34145      */
34146     view : false,
34147     
34148       /**
34149      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34150      * <pre><code>
34151
34152 layout.addxtype({
34153        xtype : 'Form',
34154        items: [ .... ]
34155    }
34156 );
34157
34158 </code></pre>
34159      * @param {Object} cfg Xtype definition of item to add.
34160      */
34161     
34162     
34163     getChildContainer: function () {
34164         return this.getEl();
34165     }
34166     
34167     
34168     /*
34169         var  ret = new Roo.factory(cfg);
34170         return ret;
34171         
34172         
34173         // add form..
34174         if (cfg.xtype.match(/^Form$/)) {
34175             
34176             var el;
34177             //if (this.footer) {
34178             //    el = this.footer.container.insertSibling(false, 'before');
34179             //} else {
34180                 el = this.el.createChild();
34181             //}
34182
34183             this.form = new  Roo.form.Form(cfg);
34184             
34185             
34186             if ( this.form.allItems.length) {
34187                 this.form.render(el.dom);
34188             }
34189             return this.form;
34190         }
34191         // should only have one of theses..
34192         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34193             // views.. should not be just added - used named prop 'view''
34194             
34195             cfg.el = this.el.appendChild(document.createElement("div"));
34196             // factory?
34197             
34198             var ret = new Roo.factory(cfg);
34199              
34200              ret.render && ret.render(false, ''); // render blank..
34201             this.view = ret;
34202             return ret;
34203         }
34204         return false;
34205     }
34206     \*/
34207 });
34208  
34209 /**
34210  * @class Roo.bootstrap.panel.Grid
34211  * @extends Roo.bootstrap.panel.Content
34212  * @constructor
34213  * Create a new GridPanel.
34214  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34215  * @param {Object} config A the config object
34216   
34217  */
34218
34219
34220
34221 Roo.bootstrap.panel.Grid = function(config)
34222 {
34223     
34224       
34225     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34226         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34227
34228     config.el = this.wrapper;
34229     //this.el = this.wrapper;
34230     
34231       if (config.container) {
34232         // ctor'ed from a Border/panel.grid
34233         
34234         
34235         this.wrapper.setStyle("overflow", "hidden");
34236         this.wrapper.addClass('roo-grid-container');
34237
34238     }
34239     
34240     
34241     if(config.toolbar){
34242         var tool_el = this.wrapper.createChild();    
34243         this.toolbar = Roo.factory(config.toolbar);
34244         var ti = [];
34245         if (config.toolbar.items) {
34246             ti = config.toolbar.items ;
34247             delete config.toolbar.items ;
34248         }
34249         
34250         var nitems = [];
34251         this.toolbar.render(tool_el);
34252         for(var i =0;i < ti.length;i++) {
34253           //  Roo.log(['add child', items[i]]);
34254             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34255         }
34256         this.toolbar.items = nitems;
34257         
34258         delete config.toolbar;
34259     }
34260     
34261     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34262     config.grid.scrollBody = true;;
34263     config.grid.monitorWindowResize = false; // turn off autosizing
34264     config.grid.autoHeight = false;
34265     config.grid.autoWidth = false;
34266     
34267     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34268     
34269     if (config.background) {
34270         // render grid on panel activation (if panel background)
34271         this.on('activate', function(gp) {
34272             if (!gp.grid.rendered) {
34273                 gp.grid.render(el);
34274                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34275
34276             }
34277         });
34278             
34279     } else {
34280         this.grid.render(this.wrapper);
34281         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34282
34283     }
34284     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34285     // ??? needed ??? config.el = this.wrapper;
34286     
34287     
34288     
34289   
34290     // xtype created footer. - not sure if will work as we normally have to render first..
34291     if (this.footer && !this.footer.el && this.footer.xtype) {
34292         
34293         var ctr = this.grid.getView().getFooterPanel(true);
34294         this.footer.dataSource = this.grid.dataSource;
34295         this.footer = Roo.factory(this.footer, Roo);
34296         this.footer.render(ctr);
34297         
34298     }
34299     
34300     
34301     
34302     
34303      
34304 };
34305
34306 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34307     getId : function(){
34308         return this.grid.id;
34309     },
34310     
34311     /**
34312      * Returns the grid for this panel
34313      * @return {Roo.bootstrap.Table} 
34314      */
34315     getGrid : function(){
34316         return this.grid;    
34317     },
34318     
34319     setSize : function(width, height){
34320         if(!this.ignoreResize(width, height)){
34321             var grid = this.grid;
34322             var size = this.adjustForComponents(width, height);
34323             var gridel = grid.getGridEl();
34324             gridel.setSize(size.width, size.height);
34325             /*
34326             var thd = grid.getGridEl().select('thead',true).first();
34327             var tbd = grid.getGridEl().select('tbody', true).first();
34328             if (tbd) {
34329                 tbd.setSize(width, height - thd.getHeight());
34330             }
34331             */
34332             grid.autoSize();
34333         }
34334     },
34335      
34336     
34337     
34338     beforeSlide : function(){
34339         this.grid.getView().scroller.clip();
34340     },
34341     
34342     afterSlide : function(){
34343         this.grid.getView().scroller.unclip();
34344     },
34345     
34346     destroy : function(){
34347         this.grid.destroy();
34348         delete this.grid;
34349         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34350     }
34351 });
34352
34353 /**
34354  * @class Roo.bootstrap.panel.Nest
34355  * @extends Roo.bootstrap.panel.Content
34356  * @constructor
34357  * Create a new Panel, that can contain a layout.Border.
34358  * 
34359  * 
34360  * @param {Roo.BorderLayout} layout The layout for this panel
34361  * @param {String/Object} config A string to set only the title or a config object
34362  */
34363 Roo.bootstrap.panel.Nest = function(config)
34364 {
34365     // construct with only one argument..
34366     /* FIXME - implement nicer consturctors
34367     if (layout.layout) {
34368         config = layout;
34369         layout = config.layout;
34370         delete config.layout;
34371     }
34372     if (layout.xtype && !layout.getEl) {
34373         // then layout needs constructing..
34374         layout = Roo.factory(layout, Roo);
34375     }
34376     */
34377     
34378     config.el =  config.layout.getEl();
34379     
34380     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34381     
34382     config.layout.monitorWindowResize = false; // turn off autosizing
34383     this.layout = config.layout;
34384     this.layout.getEl().addClass("roo-layout-nested-layout");
34385     
34386     
34387     
34388     
34389 };
34390
34391 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34392
34393     setSize : function(width, height){
34394         if(!this.ignoreResize(width, height)){
34395             var size = this.adjustForComponents(width, height);
34396             var el = this.layout.getEl();
34397             el.setSize(size.width, size.height);
34398             var touch = el.dom.offsetWidth;
34399             this.layout.layout();
34400             // ie requires a double layout on the first pass
34401             if(Roo.isIE && !this.initialized){
34402                 this.initialized = true;
34403                 this.layout.layout();
34404             }
34405         }
34406     },
34407     
34408     // activate all subpanels if not currently active..
34409     
34410     setActiveState : function(active){
34411         this.active = active;
34412         if(!active){
34413             this.fireEvent("deactivate", this);
34414             return;
34415         }
34416         
34417         this.fireEvent("activate", this);
34418         // not sure if this should happen before or after..
34419         if (!this.layout) {
34420             return; // should not happen..
34421         }
34422         var reg = false;
34423         for (var r in this.layout.regions) {
34424             reg = this.layout.getRegion(r);
34425             if (reg.getActivePanel()) {
34426                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34427                 reg.setActivePanel(reg.getActivePanel());
34428                 continue;
34429             }
34430             if (!reg.panels.length) {
34431                 continue;
34432             }
34433             reg.showPanel(reg.getPanel(0));
34434         }
34435         
34436         
34437         
34438         
34439     },
34440     
34441     /**
34442      * Returns the nested BorderLayout for this panel
34443      * @return {Roo.BorderLayout} 
34444      */
34445     getLayout : function(){
34446         return this.layout;
34447     },
34448     
34449      /**
34450      * Adds a xtype elements to the layout of the nested panel
34451      * <pre><code>
34452
34453 panel.addxtype({
34454        xtype : 'ContentPanel',
34455        region: 'west',
34456        items: [ .... ]
34457    }
34458 );
34459
34460 panel.addxtype({
34461         xtype : 'NestedLayoutPanel',
34462         region: 'west',
34463         layout: {
34464            center: { },
34465            west: { }   
34466         },
34467         items : [ ... list of content panels or nested layout panels.. ]
34468    }
34469 );
34470 </code></pre>
34471      * @param {Object} cfg Xtype definition of item to add.
34472      */
34473     addxtype : function(cfg) {
34474         return this.layout.addxtype(cfg);
34475     
34476     }
34477 });        /*
34478  * Based on:
34479  * Ext JS Library 1.1.1
34480  * Copyright(c) 2006-2007, Ext JS, LLC.
34481  *
34482  * Originally Released Under LGPL - original licence link has changed is not relivant.
34483  *
34484  * Fork - LGPL
34485  * <script type="text/javascript">
34486  */
34487 /**
34488  * @class Roo.TabPanel
34489  * @extends Roo.util.Observable
34490  * A lightweight tab container.
34491  * <br><br>
34492  * Usage:
34493  * <pre><code>
34494 // basic tabs 1, built from existing content
34495 var tabs = new Roo.TabPanel("tabs1");
34496 tabs.addTab("script", "View Script");
34497 tabs.addTab("markup", "View Markup");
34498 tabs.activate("script");
34499
34500 // more advanced tabs, built from javascript
34501 var jtabs = new Roo.TabPanel("jtabs");
34502 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34503
34504 // set up the UpdateManager
34505 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34506 var updater = tab2.getUpdateManager();
34507 updater.setDefaultUrl("ajax1.htm");
34508 tab2.on('activate', updater.refresh, updater, true);
34509
34510 // Use setUrl for Ajax loading
34511 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34512 tab3.setUrl("ajax2.htm", null, true);
34513
34514 // Disabled tab
34515 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34516 tab4.disable();
34517
34518 jtabs.activate("jtabs-1");
34519  * </code></pre>
34520  * @constructor
34521  * Create a new TabPanel.
34522  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34523  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34524  */
34525 Roo.bootstrap.panel.Tabs = function(config){
34526     /**
34527     * The container element for this TabPanel.
34528     * @type Roo.Element
34529     */
34530     this.el = Roo.get(config.el);
34531     delete config.el;
34532     if(config){
34533         if(typeof config == "boolean"){
34534             this.tabPosition = config ? "bottom" : "top";
34535         }else{
34536             Roo.apply(this, config);
34537         }
34538     }
34539     
34540     if(this.tabPosition == "bottom"){
34541         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34542         this.el.addClass("roo-tabs-bottom");
34543     }
34544     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34545     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34546     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34547     if(Roo.isIE){
34548         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34549     }
34550     if(this.tabPosition != "bottom"){
34551         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34552          * @type Roo.Element
34553          */
34554         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34555         this.el.addClass("roo-tabs-top");
34556     }
34557     this.items = [];
34558
34559     this.bodyEl.setStyle("position", "relative");
34560
34561     this.active = null;
34562     this.activateDelegate = this.activate.createDelegate(this);
34563
34564     this.addEvents({
34565         /**
34566          * @event tabchange
34567          * Fires when the active tab changes
34568          * @param {Roo.TabPanel} this
34569          * @param {Roo.TabPanelItem} activePanel The new active tab
34570          */
34571         "tabchange": true,
34572         /**
34573          * @event beforetabchange
34574          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34575          * @param {Roo.TabPanel} this
34576          * @param {Object} e Set cancel to true on this object to cancel the tab change
34577          * @param {Roo.TabPanelItem} tab The tab being changed to
34578          */
34579         "beforetabchange" : true
34580     });
34581
34582     Roo.EventManager.onWindowResize(this.onResize, this);
34583     this.cpad = this.el.getPadding("lr");
34584     this.hiddenCount = 0;
34585
34586
34587     // toolbar on the tabbar support...
34588     if (this.toolbar) {
34589         alert("no toolbar support yet");
34590         this.toolbar  = false;
34591         /*
34592         var tcfg = this.toolbar;
34593         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34594         this.toolbar = new Roo.Toolbar(tcfg);
34595         if (Roo.isSafari) {
34596             var tbl = tcfg.container.child('table', true);
34597             tbl.setAttribute('width', '100%');
34598         }
34599         */
34600         
34601     }
34602    
34603
34604
34605     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34606 };
34607
34608 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34609     /*
34610      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34611      */
34612     tabPosition : "top",
34613     /*
34614      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34615      */
34616     currentTabWidth : 0,
34617     /*
34618      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34619      */
34620     minTabWidth : 40,
34621     /*
34622      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34623      */
34624     maxTabWidth : 250,
34625     /*
34626      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34627      */
34628     preferredTabWidth : 175,
34629     /*
34630      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34631      */
34632     resizeTabs : false,
34633     /*
34634      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34635      */
34636     monitorResize : true,
34637     /*
34638      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34639      */
34640     toolbar : false,
34641
34642     /**
34643      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34644      * @param {String} id The id of the div to use <b>or create</b>
34645      * @param {String} text The text for the tab
34646      * @param {String} content (optional) Content to put in the TabPanelItem body
34647      * @param {Boolean} closable (optional) True to create a close icon on the tab
34648      * @return {Roo.TabPanelItem} The created TabPanelItem
34649      */
34650     addTab : function(id, text, content, closable)
34651     {
34652         var item = new Roo.bootstrap.panel.TabItem({
34653             panel: this,
34654             id : id,
34655             text : text,
34656             closable : closable
34657         });
34658         this.addTabItem(item);
34659         if(content){
34660             item.setContent(content);
34661         }
34662         return item;
34663     },
34664
34665     /**
34666      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34667      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34668      * @return {Roo.TabPanelItem}
34669      */
34670     getTab : function(id){
34671         return this.items[id];
34672     },
34673
34674     /**
34675      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34676      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34677      */
34678     hideTab : function(id){
34679         var t = this.items[id];
34680         if(!t.isHidden()){
34681            t.setHidden(true);
34682            this.hiddenCount++;
34683            this.autoSizeTabs();
34684         }
34685     },
34686
34687     /**
34688      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34689      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34690      */
34691     unhideTab : function(id){
34692         var t = this.items[id];
34693         if(t.isHidden()){
34694            t.setHidden(false);
34695            this.hiddenCount--;
34696            this.autoSizeTabs();
34697         }
34698     },
34699
34700     /**
34701      * Adds an existing {@link Roo.TabPanelItem}.
34702      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34703      */
34704     addTabItem : function(item){
34705         this.items[item.id] = item;
34706         this.items.push(item);
34707       //  if(this.resizeTabs){
34708     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34709   //         this.autoSizeTabs();
34710 //        }else{
34711 //            item.autoSize();
34712        // }
34713     },
34714
34715     /**
34716      * Removes a {@link Roo.TabPanelItem}.
34717      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34718      */
34719     removeTab : function(id){
34720         var items = this.items;
34721         var tab = items[id];
34722         if(!tab) { return; }
34723         var index = items.indexOf(tab);
34724         if(this.active == tab && items.length > 1){
34725             var newTab = this.getNextAvailable(index);
34726             if(newTab) {
34727                 newTab.activate();
34728             }
34729         }
34730         this.stripEl.dom.removeChild(tab.pnode.dom);
34731         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34732             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34733         }
34734         items.splice(index, 1);
34735         delete this.items[tab.id];
34736         tab.fireEvent("close", tab);
34737         tab.purgeListeners();
34738         this.autoSizeTabs();
34739     },
34740
34741     getNextAvailable : function(start){
34742         var items = this.items;
34743         var index = start;
34744         // look for a next tab that will slide over to
34745         // replace the one being removed
34746         while(index < items.length){
34747             var item = items[++index];
34748             if(item && !item.isHidden()){
34749                 return item;
34750             }
34751         }
34752         // if one isn't found select the previous tab (on the left)
34753         index = start;
34754         while(index >= 0){
34755             var item = items[--index];
34756             if(item && !item.isHidden()){
34757                 return item;
34758             }
34759         }
34760         return null;
34761     },
34762
34763     /**
34764      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34765      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34766      */
34767     disableTab : function(id){
34768         var tab = this.items[id];
34769         if(tab && this.active != tab){
34770             tab.disable();
34771         }
34772     },
34773
34774     /**
34775      * Enables a {@link Roo.TabPanelItem} that is disabled.
34776      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34777      */
34778     enableTab : function(id){
34779         var tab = this.items[id];
34780         tab.enable();
34781     },
34782
34783     /**
34784      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34785      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34786      * @return {Roo.TabPanelItem} The TabPanelItem.
34787      */
34788     activate : function(id){
34789         var tab = this.items[id];
34790         if(!tab){
34791             return null;
34792         }
34793         if(tab == this.active || tab.disabled){
34794             return tab;
34795         }
34796         var e = {};
34797         this.fireEvent("beforetabchange", this, e, tab);
34798         if(e.cancel !== true && !tab.disabled){
34799             if(this.active){
34800                 this.active.hide();
34801             }
34802             this.active = this.items[id];
34803             this.active.show();
34804             this.fireEvent("tabchange", this, this.active);
34805         }
34806         return tab;
34807     },
34808
34809     /**
34810      * Gets the active {@link Roo.TabPanelItem}.
34811      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34812      */
34813     getActiveTab : function(){
34814         return this.active;
34815     },
34816
34817     /**
34818      * Updates the tab body element to fit the height of the container element
34819      * for overflow scrolling
34820      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34821      */
34822     syncHeight : function(targetHeight){
34823         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34824         var bm = this.bodyEl.getMargins();
34825         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34826         this.bodyEl.setHeight(newHeight);
34827         return newHeight;
34828     },
34829
34830     onResize : function(){
34831         if(this.monitorResize){
34832             this.autoSizeTabs();
34833         }
34834     },
34835
34836     /**
34837      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34838      */
34839     beginUpdate : function(){
34840         this.updating = true;
34841     },
34842
34843     /**
34844      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34845      */
34846     endUpdate : function(){
34847         this.updating = false;
34848         this.autoSizeTabs();
34849     },
34850
34851     /**
34852      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34853      */
34854     autoSizeTabs : function(){
34855         var count = this.items.length;
34856         var vcount = count - this.hiddenCount;
34857         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34858             return;
34859         }
34860         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34861         var availWidth = Math.floor(w / vcount);
34862         var b = this.stripBody;
34863         if(b.getWidth() > w){
34864             var tabs = this.items;
34865             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34866             if(availWidth < this.minTabWidth){
34867                 /*if(!this.sleft){    // incomplete scrolling code
34868                     this.createScrollButtons();
34869                 }
34870                 this.showScroll();
34871                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34872             }
34873         }else{
34874             if(this.currentTabWidth < this.preferredTabWidth){
34875                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34876             }
34877         }
34878     },
34879
34880     /**
34881      * Returns the number of tabs in this TabPanel.
34882      * @return {Number}
34883      */
34884      getCount : function(){
34885          return this.items.length;
34886      },
34887
34888     /**
34889      * Resizes all the tabs to the passed width
34890      * @param {Number} The new width
34891      */
34892     setTabWidth : function(width){
34893         this.currentTabWidth = width;
34894         for(var i = 0, len = this.items.length; i < len; i++) {
34895                 if(!this.items[i].isHidden()) {
34896                 this.items[i].setWidth(width);
34897             }
34898         }
34899     },
34900
34901     /**
34902      * Destroys this TabPanel
34903      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34904      */
34905     destroy : function(removeEl){
34906         Roo.EventManager.removeResizeListener(this.onResize, this);
34907         for(var i = 0, len = this.items.length; i < len; i++){
34908             this.items[i].purgeListeners();
34909         }
34910         if(removeEl === true){
34911             this.el.update("");
34912             this.el.remove();
34913         }
34914     },
34915     
34916     createStrip : function(container)
34917     {
34918         var strip = document.createElement("nav");
34919         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34920         container.appendChild(strip);
34921         return strip;
34922     },
34923     
34924     createStripList : function(strip)
34925     {
34926         // div wrapper for retard IE
34927         // returns the "tr" element.
34928         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34929         //'<div class="x-tabs-strip-wrap">'+
34930           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34931           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34932         return strip.firstChild; //.firstChild.firstChild.firstChild;
34933     },
34934     createBody : function(container)
34935     {
34936         var body = document.createElement("div");
34937         Roo.id(body, "tab-body");
34938         //Roo.fly(body).addClass("x-tabs-body");
34939         Roo.fly(body).addClass("tab-content");
34940         container.appendChild(body);
34941         return body;
34942     },
34943     createItemBody :function(bodyEl, id){
34944         var body = Roo.getDom(id);
34945         if(!body){
34946             body = document.createElement("div");
34947             body.id = id;
34948         }
34949         //Roo.fly(body).addClass("x-tabs-item-body");
34950         Roo.fly(body).addClass("tab-pane");
34951          bodyEl.insertBefore(body, bodyEl.firstChild);
34952         return body;
34953     },
34954     /** @private */
34955     createStripElements :  function(stripEl, text, closable)
34956     {
34957         var td = document.createElement("li"); // was td..
34958         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34959         //stripEl.appendChild(td);
34960         /*if(closable){
34961             td.className = "x-tabs-closable";
34962             if(!this.closeTpl){
34963                 this.closeTpl = new Roo.Template(
34964                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34965                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34966                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34967                 );
34968             }
34969             var el = this.closeTpl.overwrite(td, {"text": text});
34970             var close = el.getElementsByTagName("div")[0];
34971             var inner = el.getElementsByTagName("em")[0];
34972             return {"el": el, "close": close, "inner": inner};
34973         } else {
34974         */
34975         // not sure what this is..
34976             if(!this.tabTpl){
34977                 //this.tabTpl = new Roo.Template(
34978                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34979                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34980                 //);
34981                 this.tabTpl = new Roo.Template(
34982                    '<a href="#">' +
34983                    '<span unselectable="on"' +
34984                             (this.disableTooltips ? '' : ' title="{text}"') +
34985                             ' >{text}</span></span></a>'
34986                 );
34987                 
34988             }
34989             var el = this.tabTpl.overwrite(td, {"text": text});
34990             var inner = el.getElementsByTagName("span")[0];
34991             return {"el": el, "inner": inner};
34992         //}
34993     }
34994         
34995     
34996 });
34997
34998 /**
34999  * @class Roo.TabPanelItem
35000  * @extends Roo.util.Observable
35001  * Represents an individual item (tab plus body) in a TabPanel.
35002  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35003  * @param {String} id The id of this TabPanelItem
35004  * @param {String} text The text for the tab of this TabPanelItem
35005  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35006  */
35007 Roo.bootstrap.panel.TabItem = function(config){
35008     /**
35009      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35010      * @type Roo.TabPanel
35011      */
35012     this.tabPanel = config.panel;
35013     /**
35014      * The id for this TabPanelItem
35015      * @type String
35016      */
35017     this.id = config.id;
35018     /** @private */
35019     this.disabled = false;
35020     /** @private */
35021     this.text = config.text;
35022     /** @private */
35023     this.loaded = false;
35024     this.closable = config.closable;
35025
35026     /**
35027      * The body element for this TabPanelItem.
35028      * @type Roo.Element
35029      */
35030     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35031     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35032     this.bodyEl.setStyle("display", "block");
35033     this.bodyEl.setStyle("zoom", "1");
35034     //this.hideAction();
35035
35036     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35037     /** @private */
35038     this.el = Roo.get(els.el);
35039     this.inner = Roo.get(els.inner, true);
35040     this.textEl = Roo.get(this.el.dom.firstChild, true);
35041     this.pnode = Roo.get(els.el.parentNode, true);
35042     this.el.on("mousedown", this.onTabMouseDown, this);
35043     this.el.on("click", this.onTabClick, this);
35044     /** @private */
35045     if(config.closable){
35046         var c = Roo.get(els.close, true);
35047         c.dom.title = this.closeText;
35048         c.addClassOnOver("close-over");
35049         c.on("click", this.closeClick, this);
35050      }
35051
35052     this.addEvents({
35053          /**
35054          * @event activate
35055          * Fires when this tab becomes the active tab.
35056          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35057          * @param {Roo.TabPanelItem} this
35058          */
35059         "activate": true,
35060         /**
35061          * @event beforeclose
35062          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35063          * @param {Roo.TabPanelItem} this
35064          * @param {Object} e Set cancel to true on this object to cancel the close.
35065          */
35066         "beforeclose": true,
35067         /**
35068          * @event close
35069          * Fires when this tab is closed.
35070          * @param {Roo.TabPanelItem} this
35071          */
35072          "close": true,
35073         /**
35074          * @event deactivate
35075          * Fires when this tab is no longer the active tab.
35076          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35077          * @param {Roo.TabPanelItem} this
35078          */
35079          "deactivate" : true
35080     });
35081     this.hidden = false;
35082
35083     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35084 };
35085
35086 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35087            {
35088     purgeListeners : function(){
35089        Roo.util.Observable.prototype.purgeListeners.call(this);
35090        this.el.removeAllListeners();
35091     },
35092     /**
35093      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35094      */
35095     show : function(){
35096         this.pnode.addClass("active");
35097         this.showAction();
35098         if(Roo.isOpera){
35099             this.tabPanel.stripWrap.repaint();
35100         }
35101         this.fireEvent("activate", this.tabPanel, this);
35102     },
35103
35104     /**
35105      * Returns true if this tab is the active tab.
35106      * @return {Boolean}
35107      */
35108     isActive : function(){
35109         return this.tabPanel.getActiveTab() == this;
35110     },
35111
35112     /**
35113      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35114      */
35115     hide : function(){
35116         this.pnode.removeClass("active");
35117         this.hideAction();
35118         this.fireEvent("deactivate", this.tabPanel, this);
35119     },
35120
35121     hideAction : function(){
35122         this.bodyEl.hide();
35123         this.bodyEl.setStyle("position", "absolute");
35124         this.bodyEl.setLeft("-20000px");
35125         this.bodyEl.setTop("-20000px");
35126     },
35127
35128     showAction : function(){
35129         this.bodyEl.setStyle("position", "relative");
35130         this.bodyEl.setTop("");
35131         this.bodyEl.setLeft("");
35132         this.bodyEl.show();
35133     },
35134
35135     /**
35136      * Set the tooltip for the tab.
35137      * @param {String} tooltip The tab's tooltip
35138      */
35139     setTooltip : function(text){
35140         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35141             this.textEl.dom.qtip = text;
35142             this.textEl.dom.removeAttribute('title');
35143         }else{
35144             this.textEl.dom.title = text;
35145         }
35146     },
35147
35148     onTabClick : function(e){
35149         e.preventDefault();
35150         this.tabPanel.activate(this.id);
35151     },
35152
35153     onTabMouseDown : function(e){
35154         e.preventDefault();
35155         this.tabPanel.activate(this.id);
35156     },
35157 /*
35158     getWidth : function(){
35159         return this.inner.getWidth();
35160     },
35161
35162     setWidth : function(width){
35163         var iwidth = width - this.pnode.getPadding("lr");
35164         this.inner.setWidth(iwidth);
35165         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35166         this.pnode.setWidth(width);
35167     },
35168 */
35169     /**
35170      * Show or hide the tab
35171      * @param {Boolean} hidden True to hide or false to show.
35172      */
35173     setHidden : function(hidden){
35174         this.hidden = hidden;
35175         this.pnode.setStyle("display", hidden ? "none" : "");
35176     },
35177
35178     /**
35179      * Returns true if this tab is "hidden"
35180      * @return {Boolean}
35181      */
35182     isHidden : function(){
35183         return this.hidden;
35184     },
35185
35186     /**
35187      * Returns the text for this tab
35188      * @return {String}
35189      */
35190     getText : function(){
35191         return this.text;
35192     },
35193     /*
35194     autoSize : function(){
35195         //this.el.beginMeasure();
35196         this.textEl.setWidth(1);
35197         /*
35198          *  #2804 [new] Tabs in Roojs
35199          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35200          */
35201         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35202         //this.el.endMeasure();
35203     //},
35204
35205     /**
35206      * Sets the text for the tab (Note: this also sets the tooltip text)
35207      * @param {String} text The tab's text and tooltip
35208      */
35209     setText : function(text){
35210         this.text = text;
35211         this.textEl.update(text);
35212         this.setTooltip(text);
35213         //if(!this.tabPanel.resizeTabs){
35214         //    this.autoSize();
35215         //}
35216     },
35217     /**
35218      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35219      */
35220     activate : function(){
35221         this.tabPanel.activate(this.id);
35222     },
35223
35224     /**
35225      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35226      */
35227     disable : function(){
35228         if(this.tabPanel.active != this){
35229             this.disabled = true;
35230             this.pnode.addClass("disabled");
35231         }
35232     },
35233
35234     /**
35235      * Enables this TabPanelItem if it was previously disabled.
35236      */
35237     enable : function(){
35238         this.disabled = false;
35239         this.pnode.removeClass("disabled");
35240     },
35241
35242     /**
35243      * Sets the content for this TabPanelItem.
35244      * @param {String} content The content
35245      * @param {Boolean} loadScripts true to look for and load scripts
35246      */
35247     setContent : function(content, loadScripts){
35248         this.bodyEl.update(content, loadScripts);
35249     },
35250
35251     /**
35252      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35253      * @return {Roo.UpdateManager} The UpdateManager
35254      */
35255     getUpdateManager : function(){
35256         return this.bodyEl.getUpdateManager();
35257     },
35258
35259     /**
35260      * Set a URL to be used to load the content for this TabPanelItem.
35261      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35262      * @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)
35263      * @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)
35264      * @return {Roo.UpdateManager} The UpdateManager
35265      */
35266     setUrl : function(url, params, loadOnce){
35267         if(this.refreshDelegate){
35268             this.un('activate', this.refreshDelegate);
35269         }
35270         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35271         this.on("activate", this.refreshDelegate);
35272         return this.bodyEl.getUpdateManager();
35273     },
35274
35275     /** @private */
35276     _handleRefresh : function(url, params, loadOnce){
35277         if(!loadOnce || !this.loaded){
35278             var updater = this.bodyEl.getUpdateManager();
35279             updater.update(url, params, this._setLoaded.createDelegate(this));
35280         }
35281     },
35282
35283     /**
35284      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35285      *   Will fail silently if the setUrl method has not been called.
35286      *   This does not activate the panel, just updates its content.
35287      */
35288     refresh : function(){
35289         if(this.refreshDelegate){
35290            this.loaded = false;
35291            this.refreshDelegate();
35292         }
35293     },
35294
35295     /** @private */
35296     _setLoaded : function(){
35297         this.loaded = true;
35298     },
35299
35300     /** @private */
35301     closeClick : function(e){
35302         var o = {};
35303         e.stopEvent();
35304         this.fireEvent("beforeclose", this, o);
35305         if(o.cancel !== true){
35306             this.tabPanel.removeTab(this.id);
35307         }
35308     },
35309     /**
35310      * The text displayed in the tooltip for the close icon.
35311      * @type String
35312      */
35313     closeText : "Close this tab"
35314 });