285f72f310c494575f11e4ffe7bf1b37969c6d81
[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      * Sets the url of the image - used to update it
1519      * @param {String} url the url of the image
1520      */
1521     
1522     setSrc : function(url)
1523     {
1524         this.el.dom.src =  url;
1525     }
1526     
1527     
1528    
1529 });
1530
1531  /*
1532  * - LGPL
1533  *
1534  * image
1535  * 
1536  */
1537
1538
1539 /**
1540  * @class Roo.bootstrap.Link
1541  * @extends Roo.bootstrap.Component
1542  * Bootstrap Link Class
1543  * @cfg {String} alt image alternative text
1544  * @cfg {String} href a tag href
1545  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1546  * @cfg {String} html the content of the link.
1547  * @cfg {String} anchor name for the anchor link
1548  * @cfg {String} fa - favicon
1549
1550  * @cfg {Boolean} preventDefault (true | false) default false
1551
1552  * 
1553  * @constructor
1554  * Create a new Input
1555  * @param {Object} config The config object
1556  */
1557
1558 Roo.bootstrap.Link = function(config){
1559     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1560     
1561     this.addEvents({
1562         // img events
1563         /**
1564          * @event click
1565          * The img click event for the img.
1566          * @param {Roo.EventObject} e
1567          */
1568         "click" : true
1569     });
1570 };
1571
1572 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1573     
1574     href: false,
1575     target: false,
1576     preventDefault: false,
1577     anchor : false,
1578     alt : false,
1579     fa: false,
1580
1581
1582     getAutoCreate : function()
1583     {
1584         var html = this.html || '';
1585         
1586         if (this.fa !== false) {
1587             html = '<i class="fa fa-' + this.fa + '"></i>';
1588         }
1589         var cfg = {
1590             tag: 'a'
1591         };
1592         // anchor's do not require html/href...
1593         if (this.anchor === false) {
1594             cfg.html = html;
1595             cfg.href = this.href || '#';
1596         } else {
1597             cfg.name = this.anchor;
1598             if (this.html !== false || this.fa !== false) {
1599                 cfg.html = html;
1600             }
1601             if (this.href !== false) {
1602                 cfg.href = this.href;
1603             }
1604         }
1605         
1606         if(this.alt !== false){
1607             cfg.alt = this.alt;
1608         }
1609         
1610         
1611         if(this.target !== false) {
1612             cfg.target = this.target;
1613         }
1614         
1615         return cfg;
1616     },
1617     
1618     initEvents: function() {
1619         
1620         if(!this.href || this.preventDefault){
1621             this.el.on('click', this.onClick, this);
1622         }
1623     },
1624     
1625     onClick : function(e)
1626     {
1627         if(this.preventDefault){
1628             e.preventDefault();
1629         }
1630         //Roo.log('img onclick');
1631         this.fireEvent('click', this, e);
1632     }
1633    
1634 });
1635
1636  /*
1637  * - LGPL
1638  *
1639  * header
1640  * 
1641  */
1642
1643 /**
1644  * @class Roo.bootstrap.Header
1645  * @extends Roo.bootstrap.Component
1646  * Bootstrap Header class
1647  * @cfg {String} html content of header
1648  * @cfg {Number} level (1|2|3|4|5|6) default 1
1649  * 
1650  * @constructor
1651  * Create a new Header
1652  * @param {Object} config The config object
1653  */
1654
1655
1656 Roo.bootstrap.Header  = function(config){
1657     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1658 };
1659
1660 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1661     
1662     //href : false,
1663     html : false,
1664     level : 1,
1665     
1666     
1667     
1668     getAutoCreate : function(){
1669         
1670         
1671         
1672         var cfg = {
1673             tag: 'h' + (1 *this.level),
1674             html: this.html || ''
1675         } ;
1676         
1677         return cfg;
1678     }
1679    
1680 });
1681
1682  
1683
1684  /*
1685  * Based on:
1686  * Ext JS Library 1.1.1
1687  * Copyright(c) 2006-2007, Ext JS, LLC.
1688  *
1689  * Originally Released Under LGPL - original licence link has changed is not relivant.
1690  *
1691  * Fork - LGPL
1692  * <script type="text/javascript">
1693  */
1694  
1695 /**
1696  * @class Roo.bootstrap.MenuMgr
1697  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1698  * @singleton
1699  */
1700 Roo.bootstrap.MenuMgr = function(){
1701    var menus, active, groups = {}, attached = false, lastShow = new Date();
1702
1703    // private - called when first menu is created
1704    function init(){
1705        menus = {};
1706        active = new Roo.util.MixedCollection();
1707        Roo.get(document).addKeyListener(27, function(){
1708            if(active.length > 0){
1709                hideAll();
1710            }
1711        });
1712    }
1713
1714    // private
1715    function hideAll(){
1716        if(active && active.length > 0){
1717            var c = active.clone();
1718            c.each(function(m){
1719                m.hide();
1720            });
1721        }
1722    }
1723
1724    // private
1725    function onHide(m){
1726        active.remove(m);
1727        if(active.length < 1){
1728            Roo.get(document).un("mouseup", onMouseDown);
1729             
1730            attached = false;
1731        }
1732    }
1733
1734    // private
1735    function onShow(m){
1736        var last = active.last();
1737        lastShow = new Date();
1738        active.add(m);
1739        if(!attached){
1740           Roo.get(document).on("mouseup", onMouseDown);
1741            
1742            attached = true;
1743        }
1744        if(m.parentMenu){
1745           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1746           m.parentMenu.activeChild = m;
1747        }else if(last && last.isVisible()){
1748           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1749        }
1750    }
1751
1752    // private
1753    function onBeforeHide(m){
1754        if(m.activeChild){
1755            m.activeChild.hide();
1756        }
1757        if(m.autoHideTimer){
1758            clearTimeout(m.autoHideTimer);
1759            delete m.autoHideTimer;
1760        }
1761    }
1762
1763    // private
1764    function onBeforeShow(m){
1765        var pm = m.parentMenu;
1766        if(!pm && !m.allowOtherMenus){
1767            hideAll();
1768        }else if(pm && pm.activeChild && active != m){
1769            pm.activeChild.hide();
1770        }
1771    }
1772
1773    // private this should really trigger on mouseup..
1774    function onMouseDown(e){
1775         Roo.log("on Mouse Up");
1776         
1777         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1778             Roo.log("MenuManager hideAll");
1779             hideAll();
1780             e.stopEvent();
1781         }
1782         
1783         
1784    }
1785
1786    // private
1787    function onBeforeCheck(mi, state){
1788        if(state){
1789            var g = groups[mi.group];
1790            for(var i = 0, l = g.length; i < l; i++){
1791                if(g[i] != mi){
1792                    g[i].setChecked(false);
1793                }
1794            }
1795        }
1796    }
1797
1798    return {
1799
1800        /**
1801         * Hides all menus that are currently visible
1802         */
1803        hideAll : function(){
1804             hideAll();  
1805        },
1806
1807        // private
1808        register : function(menu){
1809            if(!menus){
1810                init();
1811            }
1812            menus[menu.id] = menu;
1813            menu.on("beforehide", onBeforeHide);
1814            menu.on("hide", onHide);
1815            menu.on("beforeshow", onBeforeShow);
1816            menu.on("show", onShow);
1817            var g = menu.group;
1818            if(g && menu.events["checkchange"]){
1819                if(!groups[g]){
1820                    groups[g] = [];
1821                }
1822                groups[g].push(menu);
1823                menu.on("checkchange", onCheck);
1824            }
1825        },
1826
1827         /**
1828          * Returns a {@link Roo.menu.Menu} object
1829          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1830          * be used to generate and return a new Menu instance.
1831          */
1832        get : function(menu){
1833            if(typeof menu == "string"){ // menu id
1834                return menus[menu];
1835            }else if(menu.events){  // menu instance
1836                return menu;
1837            }
1838            /*else if(typeof menu.length == 'number'){ // array of menu items?
1839                return new Roo.bootstrap.Menu({items:menu});
1840            }else{ // otherwise, must be a config
1841                return new Roo.bootstrap.Menu(menu);
1842            }
1843            */
1844            return false;
1845        },
1846
1847        // private
1848        unregister : function(menu){
1849            delete menus[menu.id];
1850            menu.un("beforehide", onBeforeHide);
1851            menu.un("hide", onHide);
1852            menu.un("beforeshow", onBeforeShow);
1853            menu.un("show", onShow);
1854            var g = menu.group;
1855            if(g && menu.events["checkchange"]){
1856                groups[g].remove(menu);
1857                menu.un("checkchange", onCheck);
1858            }
1859        },
1860
1861        // private
1862        registerCheckable : function(menuItem){
1863            var g = menuItem.group;
1864            if(g){
1865                if(!groups[g]){
1866                    groups[g] = [];
1867                }
1868                groups[g].push(menuItem);
1869                menuItem.on("beforecheckchange", onBeforeCheck);
1870            }
1871        },
1872
1873        // private
1874        unregisterCheckable : function(menuItem){
1875            var g = menuItem.group;
1876            if(g){
1877                groups[g].remove(menuItem);
1878                menuItem.un("beforecheckchange", onBeforeCheck);
1879            }
1880        }
1881    };
1882 }();/*
1883  * - LGPL
1884  *
1885  * menu
1886  * 
1887  */
1888
1889 /**
1890  * @class Roo.bootstrap.Menu
1891  * @extends Roo.bootstrap.Component
1892  * Bootstrap Menu class - container for MenuItems
1893  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1894  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1895  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1896  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1897  * 
1898  * @constructor
1899  * Create a new Menu
1900  * @param {Object} config The config object
1901  */
1902
1903
1904 Roo.bootstrap.Menu = function(config){
1905     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1906     if (this.registerMenu && this.type != 'treeview')  {
1907         Roo.bootstrap.MenuMgr.register(this);
1908     }
1909     this.addEvents({
1910         /**
1911          * @event beforeshow
1912          * Fires before this menu is displayed
1913          * @param {Roo.menu.Menu} this
1914          */
1915         beforeshow : true,
1916         /**
1917          * @event beforehide
1918          * Fires before this menu is hidden
1919          * @param {Roo.menu.Menu} this
1920          */
1921         beforehide : true,
1922         /**
1923          * @event show
1924          * Fires after this menu is displayed
1925          * @param {Roo.menu.Menu} this
1926          */
1927         show : true,
1928         /**
1929          * @event hide
1930          * Fires after this menu is hidden
1931          * @param {Roo.menu.Menu} this
1932          */
1933         hide : true,
1934         /**
1935          * @event click
1936          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1937          * @param {Roo.menu.Menu} this
1938          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1939          * @param {Roo.EventObject} e
1940          */
1941         click : true,
1942         /**
1943          * @event mouseover
1944          * Fires when the mouse is hovering over this menu
1945          * @param {Roo.menu.Menu} this
1946          * @param {Roo.EventObject} e
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          */
1949         mouseover : true,
1950         /**
1951          * @event mouseout
1952          * Fires when the mouse exits this menu
1953          * @param {Roo.menu.Menu} this
1954          * @param {Roo.EventObject} e
1955          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1956          */
1957         mouseout : true,
1958         /**
1959          * @event itemclick
1960          * Fires when a menu item contained in this menu is clicked
1961          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1962          * @param {Roo.EventObject} e
1963          */
1964         itemclick: true
1965     });
1966     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1967 };
1968
1969 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1970     
1971    /// html : false,
1972     //align : '',
1973     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1974     type: false,
1975     /**
1976      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1977      */
1978     registerMenu : true,
1979     
1980     menuItems :false, // stores the menu items..
1981     
1982     hidden:true,
1983         
1984     parentMenu : false,
1985     
1986     stopEvent : true,
1987     
1988     isLink : false,
1989     
1990     getChildContainer : function() {
1991         return this.el;  
1992     },
1993     
1994     getAutoCreate : function(){
1995          
1996         //if (['right'].indexOf(this.align)!==-1) {
1997         //    cfg.cn[1].cls += ' pull-right'
1998         //}
1999         
2000         
2001         var cfg = {
2002             tag : 'ul',
2003             cls : 'dropdown-menu' ,
2004             style : 'z-index:1000'
2005             
2006         };
2007         
2008         if (this.type === 'submenu') {
2009             cfg.cls = 'submenu active';
2010         }
2011         if (this.type === 'treeview') {
2012             cfg.cls = 'treeview-menu';
2013         }
2014         
2015         return cfg;
2016     },
2017     initEvents : function() {
2018         
2019        // Roo.log("ADD event");
2020        // Roo.log(this.triggerEl.dom);
2021         
2022         this.triggerEl.on('click', this.onTriggerClick, this);
2023         
2024         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2025         
2026         this.triggerEl.addClass('dropdown-toggle');
2027         
2028         if (Roo.isTouch) {
2029             this.el.on('touchstart'  , this.onTouch, this);
2030         }
2031         this.el.on('click' , this.onClick, this);
2032
2033         this.el.on("mouseover", this.onMouseOver, this);
2034         this.el.on("mouseout", this.onMouseOut, this);
2035         
2036     },
2037     
2038     findTargetItem : function(e)
2039     {
2040         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2041         if(!t){
2042             return false;
2043         }
2044         //Roo.log(t);         Roo.log(t.id);
2045         if(t && t.id){
2046             //Roo.log(this.menuitems);
2047             return this.menuitems.get(t.id);
2048             
2049             //return this.items.get(t.menuItemId);
2050         }
2051         
2052         return false;
2053     },
2054     
2055     onTouch : function(e) 
2056     {
2057         Roo.log("menu.onTouch");
2058         //e.stopEvent(); this make the user popdown broken
2059         this.onClick(e);
2060     },
2061     
2062     onClick : function(e)
2063     {
2064         Roo.log("menu.onClick");
2065         
2066         var t = this.findTargetItem(e);
2067         if(!t || t.isContainer){
2068             return;
2069         }
2070         Roo.log(e);
2071         /*
2072         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2073             if(t == this.activeItem && t.shouldDeactivate(e)){
2074                 this.activeItem.deactivate();
2075                 delete this.activeItem;
2076                 return;
2077             }
2078             if(t.canActivate){
2079                 this.setActiveItem(t, true);
2080             }
2081             return;
2082             
2083             
2084         }
2085         */
2086        
2087         Roo.log('pass click event');
2088         
2089         t.onClick(e);
2090         
2091         this.fireEvent("click", this, t, e);
2092         
2093         var _this = this;
2094         
2095         (function() { _this.hide(); }).defer(500);
2096     },
2097     
2098     onMouseOver : function(e){
2099         var t  = this.findTargetItem(e);
2100         //Roo.log(t);
2101         //if(t){
2102         //    if(t.canActivate && !t.disabled){
2103         //        this.setActiveItem(t, true);
2104         //    }
2105         //}
2106         
2107         this.fireEvent("mouseover", this, e, t);
2108     },
2109     isVisible : function(){
2110         return !this.hidden;
2111     },
2112      onMouseOut : function(e){
2113         var t  = this.findTargetItem(e);
2114         
2115         //if(t ){
2116         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2117         //        this.activeItem.deactivate();
2118         //        delete this.activeItem;
2119         //    }
2120         //}
2121         this.fireEvent("mouseout", this, e, t);
2122     },
2123     
2124     
2125     /**
2126      * Displays this menu relative to another element
2127      * @param {String/HTMLElement/Roo.Element} element The element to align to
2128      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2129      * the element (defaults to this.defaultAlign)
2130      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2131      */
2132     show : function(el, pos, parentMenu){
2133         this.parentMenu = parentMenu;
2134         if(!this.el){
2135             this.render();
2136         }
2137         this.fireEvent("beforeshow", this);
2138         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2139     },
2140      /**
2141      * Displays this menu at a specific xy position
2142      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2143      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2144      */
2145     showAt : function(xy, parentMenu, /* private: */_e){
2146         this.parentMenu = parentMenu;
2147         if(!this.el){
2148             this.render();
2149         }
2150         if(_e !== false){
2151             this.fireEvent("beforeshow", this);
2152             //xy = this.el.adjustForConstraints(xy);
2153         }
2154         
2155         //this.el.show();
2156         this.hideMenuItems();
2157         this.hidden = false;
2158         this.triggerEl.addClass('open');
2159         
2160         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2161             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2162         }
2163         
2164         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2165             this.el.setXY(xy);
2166         }
2167         
2168         this.focus();
2169         this.fireEvent("show", this);
2170     },
2171     
2172     focus : function(){
2173         return;
2174         if(!this.hidden){
2175             this.doFocus.defer(50, this);
2176         }
2177     },
2178
2179     doFocus : function(){
2180         if(!this.hidden){
2181             this.focusEl.focus();
2182         }
2183     },
2184
2185     /**
2186      * Hides this menu and optionally all parent menus
2187      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2188      */
2189     hide : function(deep)
2190     {
2191         
2192         this.hideMenuItems();
2193         if(this.el && this.isVisible()){
2194             this.fireEvent("beforehide", this);
2195             if(this.activeItem){
2196                 this.activeItem.deactivate();
2197                 this.activeItem = null;
2198             }
2199             this.triggerEl.removeClass('open');;
2200             this.hidden = true;
2201             this.fireEvent("hide", this);
2202         }
2203         if(deep === true && this.parentMenu){
2204             this.parentMenu.hide(true);
2205         }
2206     },
2207     
2208     onTriggerClick : function(e)
2209     {
2210         Roo.log('trigger click');
2211         
2212         var target = e.getTarget();
2213         
2214         Roo.log(target.nodeName.toLowerCase());
2215         
2216         if(target.nodeName.toLowerCase() === 'i'){
2217             e.preventDefault();
2218         }
2219         
2220     },
2221     
2222     onTriggerPress  : function(e)
2223     {
2224         Roo.log('trigger press');
2225         //Roo.log(e.getTarget());
2226        // Roo.log(this.triggerEl.dom);
2227        
2228         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2229         var pel = Roo.get(e.getTarget());
2230         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2231             Roo.log('is treeview or dropdown?');
2232             return;
2233         }
2234         
2235         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2236             return;
2237         }
2238         
2239         if (this.isVisible()) {
2240             Roo.log('hide');
2241             this.hide();
2242         } else {
2243             Roo.log('show');
2244             this.show(this.triggerEl, false, false);
2245         }
2246         
2247         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2248             e.stopEvent();
2249         }
2250         
2251     },
2252        
2253     
2254     hideMenuItems : function()
2255     {
2256         Roo.log("hide Menu Items");
2257         if (!this.el) { 
2258             return;
2259         }
2260         //$(backdrop).remove()
2261         this.el.select('.open',true).each(function(aa) {
2262             
2263             aa.removeClass('open');
2264           //var parent = getParent($(this))
2265           //var relatedTarget = { relatedTarget: this }
2266           
2267            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2268           //if (e.isDefaultPrevented()) return
2269            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2270         });
2271     },
2272     addxtypeChild : function (tree, cntr) {
2273         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2274           
2275         this.menuitems.add(comp);
2276         return comp;
2277
2278     },
2279     getEl : function()
2280     {
2281         Roo.log(this.el);
2282         return this.el;
2283     }
2284 });
2285
2286  
2287  /*
2288  * - LGPL
2289  *
2290  * menu item
2291  * 
2292  */
2293
2294
2295 /**
2296  * @class Roo.bootstrap.MenuItem
2297  * @extends Roo.bootstrap.Component
2298  * Bootstrap MenuItem class
2299  * @cfg {String} html the menu label
2300  * @cfg {String} href the link
2301  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2302  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2303  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2304  * @cfg {String} fa favicon to show on left of menu item.
2305  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2306  * 
2307  * 
2308  * @constructor
2309  * Create a new MenuItem
2310  * @param {Object} config The config object
2311  */
2312
2313
2314 Roo.bootstrap.MenuItem = function(config){
2315     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2316     this.addEvents({
2317         // raw events
2318         /**
2319          * @event click
2320          * The raw click event for the entire grid.
2321          * @param {Roo.bootstrap.MenuItem} this
2322          * @param {Roo.EventObject} e
2323          */
2324         "click" : true
2325     });
2326 };
2327
2328 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2329     
2330     href : false,
2331     html : false,
2332     preventDefault: true,
2333     isContainer : false,
2334     active : false,
2335     fa: false,
2336     
2337     getAutoCreate : function(){
2338         
2339         if(this.isContainer){
2340             return {
2341                 tag: 'li',
2342                 cls: 'dropdown-menu-item'
2343             };
2344         }
2345         var ctag = {
2346             tag: 'span',
2347             html: 'Link'
2348         };
2349         
2350         var anc = {
2351             tag : 'a',
2352             href : '#',
2353             cn : [  ]
2354         };
2355         
2356         if (this.fa !== false) {
2357             anc.cn.push({
2358                 tag : 'i',
2359                 cls : 'fa fa-' + this.fa
2360             });
2361         }
2362         
2363         anc.cn.push(ctag);
2364         
2365         
2366         var cfg= {
2367             tag: 'li',
2368             cls: 'dropdown-menu-item',
2369             cn: [ anc ]
2370         };
2371         if (this.parent().type == 'treeview') {
2372             cfg.cls = 'treeview-menu';
2373         }
2374         if (this.active) {
2375             cfg.cls += ' active';
2376         }
2377         
2378         
2379         
2380         anc.href = this.href || cfg.cn[0].href ;
2381         ctag.html = this.html || cfg.cn[0].html ;
2382         return cfg;
2383     },
2384     
2385     initEvents: function()
2386     {
2387         if (this.parent().type == 'treeview') {
2388             this.el.select('a').on('click', this.onClick, this);
2389         }
2390         if (this.menu) {
2391             this.menu.parentType = this.xtype;
2392             this.menu.triggerEl = this.el;
2393             this.menu = this.addxtype(Roo.apply({}, this.menu));
2394         }
2395         
2396     },
2397     onClick : function(e)
2398     {
2399         Roo.log('item on click ');
2400         //if(this.preventDefault){
2401         //    e.preventDefault();
2402         //}
2403         //this.parent().hideMenuItems();
2404         
2405         this.fireEvent('click', this, e);
2406     },
2407     getEl : function()
2408     {
2409         return this.el;
2410     } 
2411 });
2412
2413  
2414
2415  /*
2416  * - LGPL
2417  *
2418  * menu separator
2419  * 
2420  */
2421
2422
2423 /**
2424  * @class Roo.bootstrap.MenuSeparator
2425  * @extends Roo.bootstrap.Component
2426  * Bootstrap MenuSeparator class
2427  * 
2428  * @constructor
2429  * Create a new MenuItem
2430  * @param {Object} config The config object
2431  */
2432
2433
2434 Roo.bootstrap.MenuSeparator = function(config){
2435     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2436 };
2437
2438 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2439     
2440     getAutoCreate : function(){
2441         var cfg = {
2442             cls: 'divider',
2443             tag : 'li'
2444         };
2445         
2446         return cfg;
2447     }
2448    
2449 });
2450
2451  
2452
2453  
2454 /*
2455 * Licence: LGPL
2456 */
2457
2458 /**
2459  * @class Roo.bootstrap.Modal
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap Modal class
2462  * @cfg {String} title Title of dialog
2463  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2464  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2465  * @cfg {Boolean} specificTitle default false
2466  * @cfg {Array} buttons Array of buttons or standard button set..
2467  * @cfg {String} buttonPosition (left|right|center) default right
2468  * @cfg {Boolean} animate default true
2469  * @cfg {Boolean} allow_close default true
2470  * @cfg {Boolean} fitwindow default false
2471  * @cfg {String} size (sm|lg) default empty
2472  * 
2473  * 
2474  * @constructor
2475  * Create a new Modal Dialog
2476  * @param {Object} config The config object
2477  */
2478
2479 Roo.bootstrap.Modal = function(config){
2480     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2481     this.addEvents({
2482         // raw events
2483         /**
2484          * @event btnclick
2485          * The raw btnclick event for the button
2486          * @param {Roo.EventObject} e
2487          */
2488         "btnclick" : true
2489     });
2490     this.buttons = this.buttons || [];
2491      
2492     if (this.tmpl) {
2493         this.tmpl = Roo.factory(this.tmpl);
2494     }
2495     
2496 };
2497
2498 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2499     
2500     title : 'test dialog',
2501    
2502     buttons : false,
2503     
2504     // set on load...
2505      
2506     html: false,
2507     
2508     tmp: false,
2509     
2510     specificTitle: false,
2511     
2512     buttonPosition: 'right',
2513     
2514     allow_close : true,
2515     
2516     animate : true,
2517     
2518     fitwindow: false,
2519     
2520     
2521      // private
2522     dialogEl: false,
2523     bodyEl:  false,
2524     footerEl:  false,
2525     titleEl:  false,
2526     closeEl:  false,
2527     
2528     size: '',
2529     
2530     
2531     onRender : function(ct, position)
2532     {
2533         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2534      
2535         if(!this.el){
2536             var cfg = Roo.apply({},  this.getAutoCreate());
2537             cfg.id = Roo.id();
2538             //if(!cfg.name){
2539             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2540             //}
2541             //if (!cfg.name.length) {
2542             //    delete cfg.name;
2543            // }
2544             if (this.cls) {
2545                 cfg.cls += ' ' + this.cls;
2546             }
2547             if (this.style) {
2548                 cfg.style = this.style;
2549             }
2550             this.el = Roo.get(document.body).createChild(cfg, position);
2551         }
2552         //var type = this.el.dom.type;
2553         
2554         
2555         if(this.tabIndex !== undefined){
2556             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2557         }
2558         
2559         this.dialogEl = this.el.select('.modal-dialog',true).first();
2560         this.bodyEl = this.el.select('.modal-body',true).first();
2561         this.closeEl = this.el.select('.modal-header .close', true).first();
2562         this.footerEl = this.el.select('.modal-footer',true).first();
2563         this.titleEl = this.el.select('.modal-title',true).first();
2564         
2565         
2566          
2567         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2568         this.maskEl.enableDisplayMode("block");
2569         this.maskEl.hide();
2570         //this.el.addClass("x-dlg-modal");
2571     
2572         if (this.buttons.length) {
2573             Roo.each(this.buttons, function(bb) {
2574                 var b = Roo.apply({}, bb);
2575                 b.xns = b.xns || Roo.bootstrap;
2576                 b.xtype = b.xtype || 'Button';
2577                 if (typeof(b.listeners) == 'undefined') {
2578                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2579                 }
2580                 
2581                 var btn = Roo.factory(b);
2582                 
2583                 btn.render(this.el.select('.modal-footer div').first());
2584                 
2585             },this);
2586         }
2587         // render the children.
2588         var nitems = [];
2589         
2590         if(typeof(this.items) != 'undefined'){
2591             var items = this.items;
2592             delete this.items;
2593
2594             for(var i =0;i < items.length;i++) {
2595                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2596             }
2597         }
2598         
2599         this.items = nitems;
2600         
2601         // where are these used - they used to be body/close/footer
2602         
2603        
2604         this.initEvents();
2605         //this.el.addClass([this.fieldClass, this.cls]);
2606         
2607     },
2608     
2609     getAutoCreate : function(){
2610         
2611         
2612         var bdy = {
2613                 cls : 'modal-body',
2614                 html : this.html || ''
2615         };
2616         
2617         var title = {
2618             tag: 'h4',
2619             cls : 'modal-title',
2620             html : this.title
2621         };
2622         
2623         if(this.specificTitle){
2624             title = this.title;
2625             
2626         };
2627         
2628         var header = [];
2629         if (this.allow_close) {
2630             header.push({
2631                 tag: 'button',
2632                 cls : 'close',
2633                 html : '&times'
2634             });
2635         }
2636         
2637         header.push(title);
2638         
2639         var size = '';
2640         
2641         if(this.size.length){
2642             size = 'modal-' + this.size;
2643         }
2644         
2645         var modal = {
2646             cls: "modal",
2647             style : 'display: none',
2648             cn : [
2649                 {
2650                     cls: "modal-dialog " + size,
2651                     cn : [
2652                         {
2653                             cls : "modal-content",
2654                             cn : [
2655                                 {
2656                                     cls : 'modal-header',
2657                                     cn : header
2658                                 },
2659                                 bdy,
2660                                 {
2661                                     cls : 'modal-footer',
2662                                     cn : [
2663                                         {
2664                                             tag: 'div',
2665                                             cls: 'btn-' + this.buttonPosition
2666                                         }
2667                                     ]
2668                                     
2669                                 }
2670                                 
2671                                 
2672                             ]
2673                             
2674                         }
2675                     ]
2676                         
2677                 }
2678             ]
2679         };
2680         
2681         if(this.animate){
2682             modal.cls += ' fade';
2683         }
2684         
2685         return modal;
2686           
2687     },
2688     getChildContainer : function() {
2689          
2690          return this.bodyEl;
2691         
2692     },
2693     getButtonContainer : function() {
2694          return this.el.select('.modal-footer div',true).first();
2695         
2696     },
2697     initEvents : function()
2698     {
2699         if (this.allow_close) {
2700             this.closeEl.on('click', this.hide, this);
2701         }
2702         Roo.EventManager.onWindowResize(this.resize, this, true);
2703         
2704  
2705     },
2706     
2707     resize : function()
2708     {
2709         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2710         if (this.fitwindow) {
2711             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2712             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2713             this.setSize(w,h);
2714         }
2715     },
2716     
2717     setSize : function(w,h)
2718     {
2719         if (!w && !h) {
2720             return;
2721         }
2722         this.resizeTo(w,h);
2723     },
2724     
2725     show : function() {
2726         
2727         if (!this.rendered) {
2728             this.render();
2729         }
2730         
2731         this.el.setStyle('display', 'block');
2732         
2733         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2734             var _this = this;
2735             (function(){
2736                 this.el.addClass('in');
2737             }).defer(50, this);
2738         }else{
2739             this.el.addClass('in');
2740             
2741         }
2742         
2743         // not sure how we can show data in here.. 
2744         //if (this.tmpl) {
2745         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2746         //}
2747         
2748         Roo.get(document.body).addClass("x-body-masked");
2749         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2750         this.maskEl.show();
2751         this.el.setStyle('zIndex', '10001');
2752        
2753         this.fireEvent('show', this);
2754         
2755         this.resize();
2756         
2757         (function () {
2758             this.items.forEach( function(e) {
2759                 e.layout ? e.layout() : false;
2760                     
2761             });
2762         }).defer(100,this);
2763         
2764     },
2765     hide : function()
2766     {
2767         this.maskEl.hide();
2768         Roo.get(document.body).removeClass("x-body-masked");
2769         this.el.removeClass('in');
2770         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2771         
2772         if(this.animate){ // why
2773             var _this = this;
2774             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2775         }else{
2776             this.el.setStyle('display', 'none');
2777         }
2778         
2779         this.fireEvent('hide', this);
2780     },
2781     
2782     addButton : function(str, cb)
2783     {
2784          
2785         
2786         var b = Roo.apply({}, { html : str } );
2787         b.xns = b.xns || Roo.bootstrap;
2788         b.xtype = b.xtype || 'Button';
2789         if (typeof(b.listeners) == 'undefined') {
2790             b.listeners = { click : cb.createDelegate(this)  };
2791         }
2792         
2793         var btn = Roo.factory(b);
2794            
2795         btn.render(this.el.select('.modal-footer div').first());
2796         
2797         return btn;   
2798        
2799     },
2800     
2801     setDefaultButton : function(btn)
2802     {
2803         //this.el.select('.modal-footer').()
2804     },
2805     diff : false,
2806     
2807     resizeTo: function(w,h)
2808     {
2809         // skip.. ?? why??
2810         
2811         this.dialogEl.setWidth(w);
2812         if (this.diff === false) {
2813             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2814         }
2815         
2816         this.bodyEl.setHeight(h-this.diff);
2817         
2818         
2819     },
2820     setContentSize  : function(w, h)
2821     {
2822         
2823     },
2824     onButtonClick: function(btn,e)
2825     {
2826         //Roo.log([a,b,c]);
2827         this.fireEvent('btnclick', btn.name, e);
2828     },
2829      /**
2830      * Set the title of the Dialog
2831      * @param {String} str new Title
2832      */
2833     setTitle: function(str) {
2834         this.titleEl.dom.innerHTML = str;    
2835     },
2836     /**
2837      * Set the body of the Dialog
2838      * @param {String} str new Title
2839      */
2840     setBody: function(str) {
2841         this.bodyEl.dom.innerHTML = str;    
2842     },
2843     /**
2844      * Set the body of the Dialog using the template
2845      * @param {Obj} data - apply this data to the template and replace the body contents.
2846      */
2847     applyBody: function(obj)
2848     {
2849         if (!this.tmpl) {
2850             Roo.log("Error - using apply Body without a template");
2851             //code
2852         }
2853         this.tmpl.overwrite(this.bodyEl, obj);
2854     }
2855     
2856 });
2857
2858
2859 Roo.apply(Roo.bootstrap.Modal,  {
2860     /**
2861          * Button config that displays a single OK button
2862          * @type Object
2863          */
2864         OK :  [{
2865             name : 'ok',
2866             weight : 'primary',
2867             html : 'OK'
2868         }], 
2869         /**
2870          * Button config that displays Yes and No buttons
2871          * @type Object
2872          */
2873         YESNO : [
2874             {
2875                 name  : 'no',
2876                 html : 'No'
2877             },
2878             {
2879                 name  :'yes',
2880                 weight : 'primary',
2881                 html : 'Yes'
2882             }
2883         ],
2884         
2885         /**
2886          * Button config that displays OK and Cancel buttons
2887          * @type Object
2888          */
2889         OKCANCEL : [
2890             {
2891                name : 'cancel',
2892                 html : 'Cancel'
2893             },
2894             {
2895                 name : 'ok',
2896                 weight : 'primary',
2897                 html : 'OK'
2898             }
2899         ],
2900         /**
2901          * Button config that displays Yes, No and Cancel buttons
2902          * @type Object
2903          */
2904         YESNOCANCEL : [
2905             {
2906                 name : 'yes',
2907                 weight : 'primary',
2908                 html : 'Yes'
2909             },
2910             {
2911                 name : 'no',
2912                 html : 'No'
2913             },
2914             {
2915                 name : 'cancel',
2916                 html : 'Cancel'
2917             }
2918         ]
2919 });
2920  
2921  /*
2922  * - LGPL
2923  *
2924  * messagebox - can be used as a replace
2925  * 
2926  */
2927 /**
2928  * @class Roo.MessageBox
2929  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2930  * Example usage:
2931  *<pre><code>
2932 // Basic alert:
2933 Roo.Msg.alert('Status', 'Changes saved successfully.');
2934
2935 // Prompt for user data:
2936 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2937     if (btn == 'ok'){
2938         // process text value...
2939     }
2940 });
2941
2942 // Show a dialog using config options:
2943 Roo.Msg.show({
2944    title:'Save Changes?',
2945    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2946    buttons: Roo.Msg.YESNOCANCEL,
2947    fn: processResult,
2948    animEl: 'elId'
2949 });
2950 </code></pre>
2951  * @singleton
2952  */
2953 Roo.bootstrap.MessageBox = function(){
2954     var dlg, opt, mask, waitTimer;
2955     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2956     var buttons, activeTextEl, bwidth;
2957
2958     
2959     // private
2960     var handleButton = function(button){
2961         dlg.hide();
2962         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2963     };
2964
2965     // private
2966     var handleHide = function(){
2967         if(opt && opt.cls){
2968             dlg.el.removeClass(opt.cls);
2969         }
2970         //if(waitTimer){
2971         //    Roo.TaskMgr.stop(waitTimer);
2972         //    waitTimer = null;
2973         //}
2974     };
2975
2976     // private
2977     var updateButtons = function(b){
2978         var width = 0;
2979         if(!b){
2980             buttons["ok"].hide();
2981             buttons["cancel"].hide();
2982             buttons["yes"].hide();
2983             buttons["no"].hide();
2984             //dlg.footer.dom.style.display = 'none';
2985             return width;
2986         }
2987         dlg.footerEl.dom.style.display = '';
2988         for(var k in buttons){
2989             if(typeof buttons[k] != "function"){
2990                 if(b[k]){
2991                     buttons[k].show();
2992                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2993                     width += buttons[k].el.getWidth()+15;
2994                 }else{
2995                     buttons[k].hide();
2996                 }
2997             }
2998         }
2999         return width;
3000     };
3001
3002     // private
3003     var handleEsc = function(d, k, e){
3004         if(opt && opt.closable !== false){
3005             dlg.hide();
3006         }
3007         if(e){
3008             e.stopEvent();
3009         }
3010     };
3011
3012     return {
3013         /**
3014          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3015          * @return {Roo.BasicDialog} The BasicDialog element
3016          */
3017         getDialog : function(){
3018            if(!dlg){
3019                 dlg = new Roo.bootstrap.Modal( {
3020                     //draggable: true,
3021                     //resizable:false,
3022                     //constraintoviewport:false,
3023                     //fixedcenter:true,
3024                     //collapsible : false,
3025                     //shim:true,
3026                     //modal: true,
3027                   //  width:400,
3028                   //  height:100,
3029                     //buttonAlign:"center",
3030                     closeClick : function(){
3031                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3032                             handleButton("no");
3033                         }else{
3034                             handleButton("cancel");
3035                         }
3036                     }
3037                 });
3038                 dlg.render();
3039                 dlg.on("hide", handleHide);
3040                 mask = dlg.mask;
3041                 //dlg.addKeyListener(27, handleEsc);
3042                 buttons = {};
3043                 this.buttons = buttons;
3044                 var bt = this.buttonText;
3045                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3046                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3047                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3048                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3049                 //Roo.log(buttons);
3050                 bodyEl = dlg.bodyEl.createChild({
3051
3052                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3053                         '<textarea class="roo-mb-textarea"></textarea>' +
3054                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3055                 });
3056                 msgEl = bodyEl.dom.firstChild;
3057                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3058                 textboxEl.enableDisplayMode();
3059                 textboxEl.addKeyListener([10,13], function(){
3060                     if(dlg.isVisible() && opt && opt.buttons){
3061                         if(opt.buttons.ok){
3062                             handleButton("ok");
3063                         }else if(opt.buttons.yes){
3064                             handleButton("yes");
3065                         }
3066                     }
3067                 });
3068                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3069                 textareaEl.enableDisplayMode();
3070                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3071                 progressEl.enableDisplayMode();
3072                 var pf = progressEl.dom.firstChild;
3073                 if (pf) {
3074                     pp = Roo.get(pf.firstChild);
3075                     pp.setHeight(pf.offsetHeight);
3076                 }
3077                 
3078             }
3079             return dlg;
3080         },
3081
3082         /**
3083          * Updates the message box body text
3084          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3085          * the XHTML-compliant non-breaking space character '&amp;#160;')
3086          * @return {Roo.MessageBox} This message box
3087          */
3088         updateText : function(text){
3089             if(!dlg.isVisible() && !opt.width){
3090                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3091             }
3092             msgEl.innerHTML = text || '&#160;';
3093       
3094             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3095             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3096             var w = Math.max(
3097                     Math.min(opt.width || cw , this.maxWidth), 
3098                     Math.max(opt.minWidth || this.minWidth, bwidth)
3099             );
3100             if(opt.prompt){
3101                 activeTextEl.setWidth(w);
3102             }
3103             if(dlg.isVisible()){
3104                 dlg.fixedcenter = false;
3105             }
3106             // to big, make it scroll. = But as usual stupid IE does not support
3107             // !important..
3108             
3109             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3110                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3111                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3112             } else {
3113                 bodyEl.dom.style.height = '';
3114                 bodyEl.dom.style.overflowY = '';
3115             }
3116             if (cw > w) {
3117                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3118             } else {
3119                 bodyEl.dom.style.overflowX = '';
3120             }
3121             
3122             dlg.setContentSize(w, bodyEl.getHeight());
3123             if(dlg.isVisible()){
3124                 dlg.fixedcenter = true;
3125             }
3126             return this;
3127         },
3128
3129         /**
3130          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3131          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3132          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3133          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         updateProgress : function(value, text){
3137             if(text){
3138                 this.updateText(text);
3139             }
3140             if (pp) { // weird bug on my firefox - for some reason this is not defined
3141                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3142             }
3143             return this;
3144         },        
3145
3146         /**
3147          * Returns true if the message box is currently displayed
3148          * @return {Boolean} True if the message box is visible, else false
3149          */
3150         isVisible : function(){
3151             return dlg && dlg.isVisible();  
3152         },
3153
3154         /**
3155          * Hides the message box if it is displayed
3156          */
3157         hide : function(){
3158             if(this.isVisible()){
3159                 dlg.hide();
3160             }  
3161         },
3162
3163         /**
3164          * Displays a new message box, or reinitializes an existing message box, based on the config options
3165          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3166          * The following config object properties are supported:
3167          * <pre>
3168 Property    Type             Description
3169 ----------  ---------------  ------------------------------------------------------------------------------------
3170 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3171                                    closes (defaults to undefined)
3172 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3173                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3174 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3175                                    progress and wait dialogs will ignore this property and always hide the
3176                                    close button as they can only be closed programmatically.
3177 cls               String           A custom CSS class to apply to the message box element
3178 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3179                                    displayed (defaults to 75)
3180 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3181                                    function will be btn (the name of the button that was clicked, if applicable,
3182                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3183                                    Progress and wait dialogs will ignore this option since they do not respond to
3184                                    user actions and can only be closed programmatically, so any required function
3185                                    should be called by the same code after it closes the dialog.
3186 icon              String           A CSS class that provides a background image to be used as an icon for
3187                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3188 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3189 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3190 modal             Boolean          False to allow user interaction with the page while the message box is
3191                                    displayed (defaults to true)
3192 msg               String           A string that will replace the existing message box body text (defaults
3193                                    to the XHTML-compliant non-breaking space character '&#160;')
3194 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3195 progress          Boolean          True to display a progress bar (defaults to false)
3196 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3197 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3198 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3199 title             String           The title text
3200 value             String           The string value to set into the active textbox element if displayed
3201 wait              Boolean          True to display a progress bar (defaults to false)
3202 width             Number           The width of the dialog in pixels
3203 </pre>
3204          *
3205          * Example usage:
3206          * <pre><code>
3207 Roo.Msg.show({
3208    title: 'Address',
3209    msg: 'Please enter your address:',
3210    width: 300,
3211    buttons: Roo.MessageBox.OKCANCEL,
3212    multiline: true,
3213    fn: saveAddress,
3214    animEl: 'addAddressBtn'
3215 });
3216 </code></pre>
3217          * @param {Object} config Configuration options
3218          * @return {Roo.MessageBox} This message box
3219          */
3220         show : function(options)
3221         {
3222             
3223             // this causes nightmares if you show one dialog after another
3224             // especially on callbacks..
3225              
3226             if(this.isVisible()){
3227                 
3228                 this.hide();
3229                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3230                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3231                 Roo.log("New Dialog Message:" +  options.msg )
3232                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3233                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3234                 
3235             }
3236             var d = this.getDialog();
3237             opt = options;
3238             d.setTitle(opt.title || "&#160;");
3239             d.closeEl.setDisplayed(opt.closable !== false);
3240             activeTextEl = textboxEl;
3241             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3242             if(opt.prompt){
3243                 if(opt.multiline){
3244                     textboxEl.hide();
3245                     textareaEl.show();
3246                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3247                         opt.multiline : this.defaultTextHeight);
3248                     activeTextEl = textareaEl;
3249                 }else{
3250                     textboxEl.show();
3251                     textareaEl.hide();
3252                 }
3253             }else{
3254                 textboxEl.hide();
3255                 textareaEl.hide();
3256             }
3257             progressEl.setDisplayed(opt.progress === true);
3258             this.updateProgress(0);
3259             activeTextEl.dom.value = opt.value || "";
3260             if(opt.prompt){
3261                 dlg.setDefaultButton(activeTextEl);
3262             }else{
3263                 var bs = opt.buttons;
3264                 var db = null;
3265                 if(bs && bs.ok){
3266                     db = buttons["ok"];
3267                 }else if(bs && bs.yes){
3268                     db = buttons["yes"];
3269                 }
3270                 dlg.setDefaultButton(db);
3271             }
3272             bwidth = updateButtons(opt.buttons);
3273             this.updateText(opt.msg);
3274             if(opt.cls){
3275                 d.el.addClass(opt.cls);
3276             }
3277             d.proxyDrag = opt.proxyDrag === true;
3278             d.modal = opt.modal !== false;
3279             d.mask = opt.modal !== false ? mask : false;
3280             if(!d.isVisible()){
3281                 // force it to the end of the z-index stack so it gets a cursor in FF
3282                 document.body.appendChild(dlg.el.dom);
3283                 d.animateTarget = null;
3284                 d.show(options.animEl);
3285             }
3286             return this;
3287         },
3288
3289         /**
3290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3292          * and closing the message box when the process is complete.
3293          * @param {String} title The title bar text
3294          * @param {String} msg The message box body text
3295          * @return {Roo.MessageBox} This message box
3296          */
3297         progress : function(title, msg){
3298             this.show({
3299                 title : title,
3300                 msg : msg,
3301                 buttons: false,
3302                 progress:true,
3303                 closable:false,
3304                 minWidth: this.minProgressWidth,
3305                 modal : true
3306             });
3307             return this;
3308         },
3309
3310         /**
3311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3312          * If a callback function is passed it will be called after the user clicks the button, and the
3313          * id of the button that was clicked will be passed as the only parameter to the callback
3314          * (could also be the top-right close button).
3315          * @param {String} title The title bar text
3316          * @param {String} msg The message box body text
3317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3318          * @param {Object} scope (optional) The scope of the callback function
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         alert : function(title, msg, fn, scope){
3322             this.show({
3323                 title : title,
3324                 msg : msg,
3325                 buttons: this.OK,
3326                 fn: fn,
3327                 scope : scope,
3328                 modal : true
3329             });
3330             return this;
3331         },
3332
3333         /**
3334          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3335          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3336          * You are responsible for closing the message box when the process is complete.
3337          * @param {String} msg The message box body text
3338          * @param {String} title (optional) The title bar text
3339          * @return {Roo.MessageBox} This message box
3340          */
3341         wait : function(msg, title){
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: false,
3346                 closable:false,
3347                 progress:true,
3348                 modal:true,
3349                 width:300,
3350                 wait:true
3351             });
3352             waitTimer = Roo.TaskMgr.start({
3353                 run: function(i){
3354                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3355                 },
3356                 interval: 1000
3357             });
3358             return this;
3359         },
3360
3361         /**
3362          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3363          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3364          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3365          * @param {String} title The title bar text
3366          * @param {String} msg The message box body text
3367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3368          * @param {Object} scope (optional) The scope of the callback function
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         confirm : function(title, msg, fn, scope){
3372             this.show({
3373                 title : title,
3374                 msg : msg,
3375                 buttons: this.YESNO,
3376                 fn: fn,
3377                 scope : scope,
3378                 modal : true
3379             });
3380             return this;
3381         },
3382
3383         /**
3384          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3385          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3386          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3387          * (could also be the top-right close button) and the text that was entered will be passed as the two
3388          * parameters to the callback.
3389          * @param {String} title The title bar text
3390          * @param {String} msg The message box body text
3391          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3392          * @param {Object} scope (optional) The scope of the callback function
3393          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3394          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3395          * @return {Roo.MessageBox} This message box
3396          */
3397         prompt : function(title, msg, fn, scope, multiline){
3398             this.show({
3399                 title : title,
3400                 msg : msg,
3401                 buttons: this.OKCANCEL,
3402                 fn: fn,
3403                 minWidth:250,
3404                 scope : scope,
3405                 prompt:true,
3406                 multiline: multiline,
3407                 modal : true
3408             });
3409             return this;
3410         },
3411
3412         /**
3413          * Button config that displays a single OK button
3414          * @type Object
3415          */
3416         OK : {ok:true},
3417         /**
3418          * Button config that displays Yes and No buttons
3419          * @type Object
3420          */
3421         YESNO : {yes:true, no:true},
3422         /**
3423          * Button config that displays OK and Cancel buttons
3424          * @type Object
3425          */
3426         OKCANCEL : {ok:true, cancel:true},
3427         /**
3428          * Button config that displays Yes, No and Cancel buttons
3429          * @type Object
3430          */
3431         YESNOCANCEL : {yes:true, no:true, cancel:true},
3432
3433         /**
3434          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3435          * @type Number
3436          */
3437         defaultTextHeight : 75,
3438         /**
3439          * The maximum width in pixels of the message box (defaults to 600)
3440          * @type Number
3441          */
3442         maxWidth : 600,
3443         /**
3444          * The minimum width in pixels of the message box (defaults to 100)
3445          * @type Number
3446          */
3447         minWidth : 100,
3448         /**
3449          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3450          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3451          * @type Number
3452          */
3453         minProgressWidth : 250,
3454         /**
3455          * An object containing the default button text strings that can be overriden for localized language support.
3456          * Supported properties are: ok, cancel, yes and no.
3457          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3458          * @type Object
3459          */
3460         buttonText : {
3461             ok : "OK",
3462             cancel : "Cancel",
3463             yes : "Yes",
3464             no : "No"
3465         }
3466     };
3467 }();
3468
3469 /**
3470  * Shorthand for {@link Roo.MessageBox}
3471  */
3472 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3473 Roo.Msg = Roo.Msg || Roo.MessageBox;
3474 /*
3475  * - LGPL
3476  *
3477  * navbar
3478  * 
3479  */
3480
3481 /**
3482  * @class Roo.bootstrap.Navbar
3483  * @extends Roo.bootstrap.Component
3484  * Bootstrap Navbar class
3485
3486  * @constructor
3487  * Create a new Navbar
3488  * @param {Object} config The config object
3489  */
3490
3491
3492 Roo.bootstrap.Navbar = function(config){
3493     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3494     this.addEvents({
3495         // raw events
3496         /**
3497          * @event beforetoggle
3498          * Fire before toggle the menu
3499          * @param {Roo.EventObject} e
3500          */
3501         "beforetoggle" : true
3502     });
3503 };
3504
3505 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3506     
3507     
3508    
3509     // private
3510     navItems : false,
3511     loadMask : false,
3512     
3513     
3514     getAutoCreate : function(){
3515         
3516         
3517         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3518         
3519     },
3520     
3521     initEvents :function ()
3522     {
3523         //Roo.log(this.el.select('.navbar-toggle',true));
3524         this.el.select('.navbar-toggle',true).on('click', function() {
3525             if(this.fireEvent('beforetoggle', this) !== false){
3526                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3527             }
3528             
3529         }, this);
3530         
3531         var mark = {
3532             tag: "div",
3533             cls:"x-dlg-mask"
3534         };
3535         
3536         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3537         
3538         var size = this.el.getSize();
3539         this.maskEl.setSize(size.width, size.height);
3540         this.maskEl.enableDisplayMode("block");
3541         this.maskEl.hide();
3542         
3543         if(this.loadMask){
3544             this.maskEl.show();
3545         }
3546     },
3547     
3548     
3549     getChildContainer : function()
3550     {
3551         if (this.el.select('.collapse').getCount()) {
3552             return this.el.select('.collapse',true).first();
3553         }
3554         
3555         return this.el;
3556     },
3557     
3558     mask : function()
3559     {
3560         this.maskEl.show();
3561     },
3562     
3563     unmask : function()
3564     {
3565         this.maskEl.hide();
3566     } 
3567     
3568     
3569     
3570     
3571 });
3572
3573
3574
3575  
3576
3577  /*
3578  * - LGPL
3579  *
3580  * navbar
3581  * 
3582  */
3583
3584 /**
3585  * @class Roo.bootstrap.NavSimplebar
3586  * @extends Roo.bootstrap.Navbar
3587  * Bootstrap Sidebar class
3588  *
3589  * @cfg {Boolean} inverse is inverted color
3590  * 
3591  * @cfg {String} type (nav | pills | tabs)
3592  * @cfg {Boolean} arrangement stacked | justified
3593  * @cfg {String} align (left | right) alignment
3594  * 
3595  * @cfg {Boolean} main (true|false) main nav bar? default false
3596  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3597  * 
3598  * @cfg {String} tag (header|footer|nav|div) default is nav 
3599
3600  * 
3601  * 
3602  * 
3603  * @constructor
3604  * Create a new Sidebar
3605  * @param {Object} config The config object
3606  */
3607
3608
3609 Roo.bootstrap.NavSimplebar = function(config){
3610     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3611 };
3612
3613 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3614     
3615     inverse: false,
3616     
3617     type: false,
3618     arrangement: '',
3619     align : false,
3620     
3621     
3622     
3623     main : false,
3624     
3625     
3626     tag : false,
3627     
3628     
3629     getAutoCreate : function(){
3630         
3631         
3632         var cfg = {
3633             tag : this.tag || 'div',
3634             cls : 'navbar'
3635         };
3636           
3637         
3638         cfg.cn = [
3639             {
3640                 cls: 'nav',
3641                 tag : 'ul'
3642             }
3643         ];
3644         
3645          
3646         this.type = this.type || 'nav';
3647         if (['tabs','pills'].indexOf(this.type)!==-1) {
3648             cfg.cn[0].cls += ' nav-' + this.type
3649         
3650         
3651         } else {
3652             if (this.type!=='nav') {
3653                 Roo.log('nav type must be nav/tabs/pills')
3654             }
3655             cfg.cn[0].cls += ' navbar-nav'
3656         }
3657         
3658         
3659         
3660         
3661         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3662             cfg.cn[0].cls += ' nav-' + this.arrangement;
3663         }
3664         
3665         
3666         if (this.align === 'right') {
3667             cfg.cn[0].cls += ' navbar-right';
3668         }
3669         
3670         if (this.inverse) {
3671             cfg.cls += ' navbar-inverse';
3672             
3673         }
3674         
3675         
3676         return cfg;
3677     
3678         
3679     }
3680     
3681     
3682     
3683 });
3684
3685
3686
3687  
3688
3689  
3690        /*
3691  * - LGPL
3692  *
3693  * navbar
3694  * 
3695  */
3696
3697 /**
3698  * @class Roo.bootstrap.NavHeaderbar
3699  * @extends Roo.bootstrap.NavSimplebar
3700  * Bootstrap Sidebar class
3701  *
3702  * @cfg {String} brand what is brand
3703  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3704  * @cfg {String} brand_href href of the brand
3705  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3706  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3707  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3708  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3709  * 
3710  * @constructor
3711  * Create a new Sidebar
3712  * @param {Object} config The config object
3713  */
3714
3715
3716 Roo.bootstrap.NavHeaderbar = function(config){
3717     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3718       
3719 };
3720
3721 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3722     
3723     position: '',
3724     brand: '',
3725     brand_href: false,
3726     srButton : true,
3727     autohide : false,
3728     desktopCenter : false,
3729    
3730     
3731     getAutoCreate : function(){
3732         
3733         var   cfg = {
3734             tag: this.nav || 'nav',
3735             cls: 'navbar',
3736             role: 'navigation',
3737             cn: []
3738         };
3739         
3740         var cn = cfg.cn;
3741         if (this.desktopCenter) {
3742             cn.push({cls : 'container', cn : []});
3743             cn = cn[0].cn;
3744         }
3745         
3746         if(this.srButton){
3747             cn.push({
3748                 tag: 'div',
3749                 cls: 'navbar-header',
3750                 cn: [
3751                     {
3752                         tag: 'button',
3753                         type: 'button',
3754                         cls: 'navbar-toggle',
3755                         'data-toggle': 'collapse',
3756                         cn: [
3757                             {
3758                                 tag: 'span',
3759                                 cls: 'sr-only',
3760                                 html: 'Toggle navigation'
3761                             },
3762                             {
3763                                 tag: 'span',
3764                                 cls: 'icon-bar'
3765                             },
3766                             {
3767                                 tag: 'span',
3768                                 cls: 'icon-bar'
3769                             },
3770                             {
3771                                 tag: 'span',
3772                                 cls: 'icon-bar'
3773                             }
3774                         ]
3775                     }
3776                 ]
3777             });
3778         }
3779         
3780         cn.push({
3781             tag: 'div',
3782             cls: 'collapse navbar-collapse',
3783             cn : []
3784         });
3785         
3786         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3787         
3788         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3789             cfg.cls += ' navbar-' + this.position;
3790             
3791             // tag can override this..
3792             
3793             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3794         }
3795         
3796         if (this.brand !== '') {
3797             cn[0].cn.push({
3798                 tag: 'a',
3799                 href: this.brand_href ? this.brand_href : '#',
3800                 cls: 'navbar-brand',
3801                 cn: [
3802                 this.brand
3803                 ]
3804             });
3805         }
3806         
3807         if(this.main){
3808             cfg.cls += ' main-nav';
3809         }
3810         
3811         
3812         return cfg;
3813
3814         
3815     },
3816     getHeaderChildContainer : function()
3817     {
3818         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3819             return this.el.select('.navbar-header',true).first();
3820         }
3821         
3822         return this.getChildContainer();
3823     },
3824     
3825     
3826     initEvents : function()
3827     {
3828         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3829         
3830         if (this.autohide) {
3831             
3832             var prevScroll = 0;
3833             var ft = this.el;
3834             
3835             Roo.get(document).on('scroll',function(e) {
3836                 var ns = Roo.get(document).getScroll().top;
3837                 var os = prevScroll;
3838                 prevScroll = ns;
3839                 
3840                 if(ns > os){
3841                     ft.removeClass('slideDown');
3842                     ft.addClass('slideUp');
3843                     return;
3844                 }
3845                 ft.removeClass('slideUp');
3846                 ft.addClass('slideDown');
3847                  
3848               
3849           },this);
3850         }
3851     }    
3852     
3853 });
3854
3855
3856
3857  
3858
3859  /*
3860  * - LGPL
3861  *
3862  * navbar
3863  * 
3864  */
3865
3866 /**
3867  * @class Roo.bootstrap.NavSidebar
3868  * @extends Roo.bootstrap.Navbar
3869  * Bootstrap Sidebar class
3870  * 
3871  * @constructor
3872  * Create a new Sidebar
3873  * @param {Object} config The config object
3874  */
3875
3876
3877 Roo.bootstrap.NavSidebar = function(config){
3878     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3879 };
3880
3881 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3882     
3883     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3884     
3885     getAutoCreate : function(){
3886         
3887         
3888         return  {
3889             tag: 'div',
3890             cls: 'sidebar sidebar-nav'
3891         };
3892     
3893         
3894     }
3895     
3896     
3897     
3898 });
3899
3900
3901
3902  
3903
3904  /*
3905  * - LGPL
3906  *
3907  * nav group
3908  * 
3909  */
3910
3911 /**
3912  * @class Roo.bootstrap.NavGroup
3913  * @extends Roo.bootstrap.Component
3914  * Bootstrap NavGroup class
3915  * @cfg {String} align (left|right)
3916  * @cfg {Boolean} inverse
3917  * @cfg {String} type (nav|pills|tab) default nav
3918  * @cfg {String} navId - reference Id for navbar.
3919
3920  * 
3921  * @constructor
3922  * Create a new nav group
3923  * @param {Object} config The config object
3924  */
3925
3926 Roo.bootstrap.NavGroup = function(config){
3927     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3928     this.navItems = [];
3929    
3930     Roo.bootstrap.NavGroup.register(this);
3931      this.addEvents({
3932         /**
3933              * @event changed
3934              * Fires when the active item changes
3935              * @param {Roo.bootstrap.NavGroup} this
3936              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3937              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3938          */
3939         'changed': true
3940      });
3941     
3942 };
3943
3944 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3945     
3946     align: '',
3947     inverse: false,
3948     form: false,
3949     type: 'nav',
3950     navId : '',
3951     // private
3952     
3953     navItems : false, 
3954     
3955     getAutoCreate : function()
3956     {
3957         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3958         
3959         cfg = {
3960             tag : 'ul',
3961             cls: 'nav' 
3962         };
3963         
3964         if (['tabs','pills'].indexOf(this.type)!==-1) {
3965             cfg.cls += ' nav-' + this.type
3966         } else {
3967             if (this.type!=='nav') {
3968                 Roo.log('nav type must be nav/tabs/pills')
3969             }
3970             cfg.cls += ' navbar-nav'
3971         }
3972         
3973         if (this.parent().sidebar) {
3974             cfg = {
3975                 tag: 'ul',
3976                 cls: 'dashboard-menu sidebar-menu'
3977             };
3978             
3979             return cfg;
3980         }
3981         
3982         if (this.form === true) {
3983             cfg = {
3984                 tag: 'form',
3985                 cls: 'navbar-form'
3986             };
3987             
3988             if (this.align === 'right') {
3989                 cfg.cls += ' navbar-right';
3990             } else {
3991                 cfg.cls += ' navbar-left';
3992             }
3993         }
3994         
3995         if (this.align === 'right') {
3996             cfg.cls += ' navbar-right';
3997         }
3998         
3999         if (this.inverse) {
4000             cfg.cls += ' navbar-inverse';
4001             
4002         }
4003         
4004         
4005         return cfg;
4006     },
4007     /**
4008     * sets the active Navigation item
4009     * @param {Roo.bootstrap.NavItem} the new current navitem
4010     */
4011     setActiveItem : function(item)
4012     {
4013         var prev = false;
4014         Roo.each(this.navItems, function(v){
4015             if (v == item) {
4016                 return ;
4017             }
4018             if (v.isActive()) {
4019                 v.setActive(false, true);
4020                 prev = v;
4021                 
4022             }
4023             
4024         });
4025
4026         item.setActive(true, true);
4027         this.fireEvent('changed', this, item, prev);
4028         
4029         
4030     },
4031     /**
4032     * gets the active Navigation item
4033     * @return {Roo.bootstrap.NavItem} the current navitem
4034     */
4035     getActive : function()
4036     {
4037         
4038         var prev = false;
4039         Roo.each(this.navItems, function(v){
4040             
4041             if (v.isActive()) {
4042                 prev = v;
4043                 
4044             }
4045             
4046         });
4047         return prev;
4048     },
4049     
4050     indexOfNav : function()
4051     {
4052         
4053         var prev = false;
4054         Roo.each(this.navItems, function(v,i){
4055             
4056             if (v.isActive()) {
4057                 prev = i;
4058                 
4059             }
4060             
4061         });
4062         return prev;
4063     },
4064     /**
4065     * adds a Navigation item
4066     * @param {Roo.bootstrap.NavItem} the navitem to add
4067     */
4068     addItem : function(cfg)
4069     {
4070         var cn = new Roo.bootstrap.NavItem(cfg);
4071         this.register(cn);
4072         cn.parentId = this.id;
4073         cn.onRender(this.el, null);
4074         return cn;
4075     },
4076     /**
4077     * register a Navigation item
4078     * @param {Roo.bootstrap.NavItem} the navitem to add
4079     */
4080     register : function(item)
4081     {
4082         this.navItems.push( item);
4083         item.navId = this.navId;
4084     
4085     },
4086     
4087     /**
4088     * clear all the Navigation item
4089     */
4090    
4091     clearAll : function()
4092     {
4093         this.navItems = [];
4094         this.el.dom.innerHTML = '';
4095     },
4096     
4097     getNavItem: function(tabId)
4098     {
4099         var ret = false;
4100         Roo.each(this.navItems, function(e) {
4101             if (e.tabId == tabId) {
4102                ret =  e;
4103                return false;
4104             }
4105             return true;
4106             
4107         });
4108         return ret;
4109     },
4110     
4111     setActiveNext : function()
4112     {
4113         var i = this.indexOfNav(this.getActive());
4114         if (i > this.navItems.length) {
4115             return;
4116         }
4117         this.setActiveItem(this.navItems[i+1]);
4118     },
4119     setActivePrev : function()
4120     {
4121         var i = this.indexOfNav(this.getActive());
4122         if (i  < 1) {
4123             return;
4124         }
4125         this.setActiveItem(this.navItems[i-1]);
4126     },
4127     clearWasActive : function(except) {
4128         Roo.each(this.navItems, function(e) {
4129             if (e.tabId != except.tabId && e.was_active) {
4130                e.was_active = false;
4131                return false;
4132             }
4133             return true;
4134             
4135         });
4136     },
4137     getWasActive : function ()
4138     {
4139         var r = false;
4140         Roo.each(this.navItems, function(e) {
4141             if (e.was_active) {
4142                r = e;
4143                return false;
4144             }
4145             return true;
4146             
4147         });
4148         return r;
4149     }
4150     
4151     
4152 });
4153
4154  
4155 Roo.apply(Roo.bootstrap.NavGroup, {
4156     
4157     groups: {},
4158      /**
4159     * register a Navigation Group
4160     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4161     */
4162     register : function(navgrp)
4163     {
4164         this.groups[navgrp.navId] = navgrp;
4165         
4166     },
4167     /**
4168     * fetch a Navigation Group based on the navigation ID
4169     * @param {string} the navgroup to add
4170     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4171     */
4172     get: function(navId) {
4173         if (typeof(this.groups[navId]) == 'undefined') {
4174             return false;
4175             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4176         }
4177         return this.groups[navId] ;
4178     }
4179     
4180     
4181     
4182 });
4183
4184  /*
4185  * - LGPL
4186  *
4187  * row
4188  * 
4189  */
4190
4191 /**
4192  * @class Roo.bootstrap.NavItem
4193  * @extends Roo.bootstrap.Component
4194  * Bootstrap Navbar.NavItem class
4195  * @cfg {String} href  link to
4196  * @cfg {String} html content of button
4197  * @cfg {String} badge text inside badge
4198  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4199  * @cfg {String} glyphicon name of glyphicon
4200  * @cfg {String} icon name of font awesome icon
4201  * @cfg {Boolean} active Is item active
4202  * @cfg {Boolean} disabled Is item disabled
4203  
4204  * @cfg {Boolean} preventDefault (true | false) default false
4205  * @cfg {String} tabId the tab that this item activates.
4206  * @cfg {String} tagtype (a|span) render as a href or span?
4207  * @cfg {Boolean} animateRef (true|false) link to element default false  
4208   
4209  * @constructor
4210  * Create a new Navbar Item
4211  * @param {Object} config The config object
4212  */
4213 Roo.bootstrap.NavItem = function(config){
4214     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4215     this.addEvents({
4216         // raw events
4217         /**
4218          * @event click
4219          * The raw click event for the entire grid.
4220          * @param {Roo.EventObject} e
4221          */
4222         "click" : true,
4223          /**
4224             * @event changed
4225             * Fires when the active item active state changes
4226             * @param {Roo.bootstrap.NavItem} this
4227             * @param {boolean} state the new state
4228              
4229          */
4230         'changed': true,
4231         /**
4232             * @event scrollto
4233             * Fires when scroll to element
4234             * @param {Roo.bootstrap.NavItem} this
4235             * @param {Object} options
4236             * @param {Roo.EventObject} e
4237              
4238          */
4239         'scrollto': true
4240     });
4241    
4242 };
4243
4244 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4245     
4246     href: false,
4247     html: '',
4248     badge: '',
4249     icon: false,
4250     glyphicon: false,
4251     active: false,
4252     preventDefault : false,
4253     tabId : false,
4254     tagtype : 'a',
4255     disabled : false,
4256     animateRef : false,
4257     was_active : false,
4258     
4259     getAutoCreate : function(){
4260          
4261         var cfg = {
4262             tag: 'li',
4263             cls: 'nav-item'
4264             
4265         };
4266         
4267         if (this.active) {
4268             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4269         }
4270         if (this.disabled) {
4271             cfg.cls += ' disabled';
4272         }
4273         
4274         if (this.href || this.html || this.glyphicon || this.icon) {
4275             cfg.cn = [
4276                 {
4277                     tag: this.tagtype,
4278                     href : this.href || "#",
4279                     html: this.html || ''
4280                 }
4281             ];
4282             
4283             if (this.icon) {
4284                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4285             }
4286
4287             if(this.glyphicon) {
4288                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4289             }
4290             
4291             if (this.menu) {
4292                 
4293                 cfg.cn[0].html += " <span class='caret'></span>";
4294              
4295             }
4296             
4297             if (this.badge !== '') {
4298                  
4299                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4300             }
4301         }
4302         
4303         
4304         
4305         return cfg;
4306     },
4307     initEvents: function() 
4308     {
4309         if (typeof (this.menu) != 'undefined') {
4310             this.menu.parentType = this.xtype;
4311             this.menu.triggerEl = this.el;
4312             this.menu = this.addxtype(Roo.apply({}, this.menu));
4313         }
4314         
4315         this.el.select('a',true).on('click', this.onClick, this);
4316         
4317         if(this.tagtype == 'span'){
4318             this.el.select('span',true).on('click', this.onClick, this);
4319         }
4320        
4321         // at this point parent should be available..
4322         this.parent().register(this);
4323     },
4324     
4325     onClick : function(e)
4326     {
4327         if (e.getTarget('.dropdown-menu-item')) {
4328             // did you click on a menu itemm.... - then don't trigger onclick..
4329             return;
4330         }
4331         
4332         if(
4333                 this.preventDefault || 
4334                 this.href == '#' 
4335         ){
4336             Roo.log("NavItem - prevent Default?");
4337             e.preventDefault();
4338         }
4339         
4340         if (this.disabled) {
4341             return;
4342         }
4343         
4344         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4345         if (tg && tg.transition) {
4346             Roo.log("waiting for the transitionend");
4347             return;
4348         }
4349         
4350         
4351         
4352         //Roo.log("fire event clicked");
4353         if(this.fireEvent('click', this, e) === false){
4354             return;
4355         };
4356         
4357         if(this.tagtype == 'span'){
4358             return;
4359         }
4360         
4361         //Roo.log(this.href);
4362         var ael = this.el.select('a',true).first();
4363         //Roo.log(ael);
4364         
4365         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4366             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4367             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4368                 return; // ignore... - it's a 'hash' to another page.
4369             }
4370             Roo.log("NavItem - prevent Default?");
4371             e.preventDefault();
4372             this.scrollToElement(e);
4373         }
4374         
4375         
4376         var p =  this.parent();
4377    
4378         if (['tabs','pills'].indexOf(p.type)!==-1) {
4379             if (typeof(p.setActiveItem) !== 'undefined') {
4380                 p.setActiveItem(this);
4381             }
4382         }
4383         
4384         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4385         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4386             // remove the collapsed menu expand...
4387             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4388         }
4389     },
4390     
4391     isActive: function () {
4392         return this.active
4393     },
4394     setActive : function(state, fire, is_was_active)
4395     {
4396         if (this.active && !state && this.navId) {
4397             this.was_active = true;
4398             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4399             if (nv) {
4400                 nv.clearWasActive(this);
4401             }
4402             
4403         }
4404         this.active = state;
4405         
4406         if (!state ) {
4407             this.el.removeClass('active');
4408         } else if (!this.el.hasClass('active')) {
4409             this.el.addClass('active');
4410         }
4411         if (fire) {
4412             this.fireEvent('changed', this, state);
4413         }
4414         
4415         // show a panel if it's registered and related..
4416         
4417         if (!this.navId || !this.tabId || !state || is_was_active) {
4418             return;
4419         }
4420         
4421         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4422         if (!tg) {
4423             return;
4424         }
4425         var pan = tg.getPanelByName(this.tabId);
4426         if (!pan) {
4427             return;
4428         }
4429         // if we can not flip to new panel - go back to old nav highlight..
4430         if (false == tg.showPanel(pan)) {
4431             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4432             if (nv) {
4433                 var onav = nv.getWasActive();
4434                 if (onav) {
4435                     onav.setActive(true, false, true);
4436                 }
4437             }
4438             
4439         }
4440         
4441         
4442         
4443     },
4444      // this should not be here...
4445     setDisabled : function(state)
4446     {
4447         this.disabled = state;
4448         if (!state ) {
4449             this.el.removeClass('disabled');
4450         } else if (!this.el.hasClass('disabled')) {
4451             this.el.addClass('disabled');
4452         }
4453         
4454     },
4455     
4456     /**
4457      * Fetch the element to display the tooltip on.
4458      * @return {Roo.Element} defaults to this.el
4459      */
4460     tooltipEl : function()
4461     {
4462         return this.el.select('' + this.tagtype + '', true).first();
4463     },
4464     
4465     scrollToElement : function(e)
4466     {
4467         var c = document.body;
4468         
4469         /*
4470          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4471          */
4472         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4473             c = document.documentElement;
4474         }
4475         
4476         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4477         
4478         if(!target){
4479             return;
4480         }
4481
4482         var o = target.calcOffsetsTo(c);
4483         
4484         var options = {
4485             target : target,
4486             value : o[1]
4487         };
4488         
4489         this.fireEvent('scrollto', this, options, e);
4490         
4491         Roo.get(c).scrollTo('top', options.value, true);
4492         
4493         return;
4494     }
4495 });
4496  
4497
4498  /*
4499  * - LGPL
4500  *
4501  * sidebar item
4502  *
4503  *  li
4504  *    <span> icon </span>
4505  *    <span> text </span>
4506  *    <span>badge </span>
4507  */
4508
4509 /**
4510  * @class Roo.bootstrap.NavSidebarItem
4511  * @extends Roo.bootstrap.NavItem
4512  * Bootstrap Navbar.NavSidebarItem class
4513  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4514  * {bool} open is the menu open
4515  * @constructor
4516  * Create a new Navbar Button
4517  * @param {Object} config The config object
4518  */
4519 Roo.bootstrap.NavSidebarItem = function(config){
4520     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4521     this.addEvents({
4522         // raw events
4523         /**
4524          * @event click
4525          * The raw click event for the entire grid.
4526          * @param {Roo.EventObject} e
4527          */
4528         "click" : true,
4529          /**
4530             * @event changed
4531             * Fires when the active item active state changes
4532             * @param {Roo.bootstrap.NavSidebarItem} this
4533             * @param {boolean} state the new state
4534              
4535          */
4536         'changed': true
4537     });
4538    
4539 };
4540
4541 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4542     
4543     badgeWeight : 'default',
4544     
4545     open: false,
4546     
4547     getAutoCreate : function(){
4548         
4549         
4550         var a = {
4551                 tag: 'a',
4552                 href : this.href || '#',
4553                 cls: '',
4554                 html : '',
4555                 cn : []
4556         };
4557         var cfg = {
4558             tag: 'li',
4559             cls: '',
4560             cn: [ a ]
4561         };
4562         var span = {
4563             tag: 'span',
4564             html : this.html || ''
4565         };
4566         
4567         
4568         if (this.active) {
4569             cfg.cls += ' active';
4570         }
4571         
4572         if (this.disabled) {
4573             cfg.cls += ' disabled';
4574         }
4575         if (this.open) {
4576             cfg.cls += ' open x-open';
4577         }
4578         // left icon..
4579         if (this.glyphicon || this.icon) {
4580             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4581             a.cn.push({ tag : 'i', cls : c }) ;
4582         }
4583         // html..
4584         a.cn.push(span);
4585         // then badge..
4586         if (this.badge !== '') {
4587             
4588             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4589         }
4590         // fi
4591         if (this.menu) {
4592             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4593             a.cls += 'dropdown-toggle treeview' ;
4594         }
4595         
4596         return cfg;
4597          
4598            
4599     },
4600     
4601     initEvents : function()
4602     { 
4603         if (typeof (this.menu) != 'undefined') {
4604             this.menu.parentType = this.xtype;
4605             this.menu.triggerEl = this.el;
4606             this.menu = this.addxtype(Roo.apply({}, this.menu));
4607         }
4608         
4609         this.el.on('click', this.onClick, this);
4610        
4611     
4612         if(this.badge !== ''){
4613  
4614             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4615         }
4616         
4617     },
4618     
4619     onClick : function(e)
4620     {
4621         if(this.disabled){
4622             e.preventDefault();
4623             return;
4624         }
4625         
4626         if(this.preventDefault){
4627             e.preventDefault();
4628         }
4629         
4630         this.fireEvent('click', this);
4631     },
4632     
4633     disable : function()
4634     {
4635         this.setDisabled(true);
4636     },
4637     
4638     enable : function()
4639     {
4640         this.setDisabled(false);
4641     },
4642     
4643     setDisabled : function(state)
4644     {
4645         if(this.disabled == state){
4646             return;
4647         }
4648         
4649         this.disabled = state;
4650         
4651         if (state) {
4652             this.el.addClass('disabled');
4653             return;
4654         }
4655         
4656         this.el.removeClass('disabled');
4657         
4658         return;
4659     },
4660     
4661     setActive : function(state)
4662     {
4663         if(this.active == state){
4664             return;
4665         }
4666         
4667         this.active = state;
4668         
4669         if (state) {
4670             this.el.addClass('active');
4671             return;
4672         }
4673         
4674         this.el.removeClass('active');
4675         
4676         return;
4677     },
4678     
4679     isActive: function () 
4680     {
4681         return this.active;
4682     },
4683     
4684     setBadge : function(str)
4685     {
4686         if(!this.badgeEl){
4687             return;
4688         }
4689         
4690         this.badgeEl.dom.innerHTML = str;
4691     }
4692     
4693    
4694      
4695  
4696 });
4697  
4698
4699  /*
4700  * - LGPL
4701  *
4702  * row
4703  * 
4704  */
4705
4706 /**
4707  * @class Roo.bootstrap.Row
4708  * @extends Roo.bootstrap.Component
4709  * Bootstrap Row class (contains columns...)
4710  * 
4711  * @constructor
4712  * Create a new Row
4713  * @param {Object} config The config object
4714  */
4715
4716 Roo.bootstrap.Row = function(config){
4717     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4718 };
4719
4720 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4721     
4722     getAutoCreate : function(){
4723        return {
4724             cls: 'row clearfix'
4725        };
4726     }
4727     
4728     
4729 });
4730
4731  
4732
4733  /*
4734  * - LGPL
4735  *
4736  * element
4737  * 
4738  */
4739
4740 /**
4741  * @class Roo.bootstrap.Element
4742  * @extends Roo.bootstrap.Component
4743  * Bootstrap Element class
4744  * @cfg {String} html contents of the element
4745  * @cfg {String} tag tag of the element
4746  * @cfg {String} cls class of the element
4747  * @cfg {Boolean} preventDefault (true|false) default false
4748  * @cfg {Boolean} clickable (true|false) default false
4749  * 
4750  * @constructor
4751  * Create a new Element
4752  * @param {Object} config The config object
4753  */
4754
4755 Roo.bootstrap.Element = function(config){
4756     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4757     
4758     this.addEvents({
4759         // raw events
4760         /**
4761          * @event click
4762          * When a element is chick
4763          * @param {Roo.bootstrap.Element} this
4764          * @param {Roo.EventObject} e
4765          */
4766         "click" : true
4767     });
4768 };
4769
4770 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4771     
4772     tag: 'div',
4773     cls: '',
4774     html: '',
4775     preventDefault: false, 
4776     clickable: false,
4777     
4778     getAutoCreate : function(){
4779         
4780         var cfg = {
4781             tag: this.tag,
4782             cls: this.cls,
4783             html: this.html
4784         };
4785         
4786         return cfg;
4787     },
4788     
4789     initEvents: function() 
4790     {
4791         Roo.bootstrap.Element.superclass.initEvents.call(this);
4792         
4793         if(this.clickable){
4794             this.el.on('click', this.onClick, this);
4795         }
4796         
4797     },
4798     
4799     onClick : function(e)
4800     {
4801         if(this.preventDefault){
4802             e.preventDefault();
4803         }
4804         
4805         this.fireEvent('click', this, e);
4806     },
4807     
4808     getValue : function()
4809     {
4810         return this.el.dom.innerHTML;
4811     },
4812     
4813     setValue : function(value)
4814     {
4815         this.el.dom.innerHTML = value;
4816     }
4817    
4818 });
4819
4820  
4821
4822  /*
4823  * - LGPL
4824  *
4825  * pagination
4826  * 
4827  */
4828
4829 /**
4830  * @class Roo.bootstrap.Pagination
4831  * @extends Roo.bootstrap.Component
4832  * Bootstrap Pagination class
4833  * @cfg {String} size xs | sm | md | lg
4834  * @cfg {Boolean} inverse false | true
4835  * 
4836  * @constructor
4837  * Create a new Pagination
4838  * @param {Object} config The config object
4839  */
4840
4841 Roo.bootstrap.Pagination = function(config){
4842     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4843 };
4844
4845 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4846     
4847     cls: false,
4848     size: false,
4849     inverse: false,
4850     
4851     getAutoCreate : function(){
4852         var cfg = {
4853             tag: 'ul',
4854                 cls: 'pagination'
4855         };
4856         if (this.inverse) {
4857             cfg.cls += ' inverse';
4858         }
4859         if (this.html) {
4860             cfg.html=this.html;
4861         }
4862         if (this.cls) {
4863             cfg.cls += " " + this.cls;
4864         }
4865         return cfg;
4866     }
4867    
4868 });
4869
4870  
4871
4872  /*
4873  * - LGPL
4874  *
4875  * Pagination item
4876  * 
4877  */
4878
4879
4880 /**
4881  * @class Roo.bootstrap.PaginationItem
4882  * @extends Roo.bootstrap.Component
4883  * Bootstrap PaginationItem class
4884  * @cfg {String} html text
4885  * @cfg {String} href the link
4886  * @cfg {Boolean} preventDefault (true | false) default true
4887  * @cfg {Boolean} active (true | false) default false
4888  * @cfg {Boolean} disabled default false
4889  * 
4890  * 
4891  * @constructor
4892  * Create a new PaginationItem
4893  * @param {Object} config The config object
4894  */
4895
4896
4897 Roo.bootstrap.PaginationItem = function(config){
4898     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4899     this.addEvents({
4900         // raw events
4901         /**
4902          * @event click
4903          * The raw click event for the entire grid.
4904          * @param {Roo.EventObject} e
4905          */
4906         "click" : true
4907     });
4908 };
4909
4910 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4911     
4912     href : false,
4913     html : false,
4914     preventDefault: true,
4915     active : false,
4916     cls : false,
4917     disabled: false,
4918     
4919     getAutoCreate : function(){
4920         var cfg= {
4921             tag: 'li',
4922             cn: [
4923                 {
4924                     tag : 'a',
4925                     href : this.href ? this.href : '#',
4926                     html : this.html ? this.html : ''
4927                 }
4928             ]
4929         };
4930         
4931         if(this.cls){
4932             cfg.cls = this.cls;
4933         }
4934         
4935         if(this.disabled){
4936             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4937         }
4938         
4939         if(this.active){
4940             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4941         }
4942         
4943         return cfg;
4944     },
4945     
4946     initEvents: function() {
4947         
4948         this.el.on('click', this.onClick, this);
4949         
4950     },
4951     onClick : function(e)
4952     {
4953         Roo.log('PaginationItem on click ');
4954         if(this.preventDefault){
4955             e.preventDefault();
4956         }
4957         
4958         if(this.disabled){
4959             return;
4960         }
4961         
4962         this.fireEvent('click', this, e);
4963     }
4964    
4965 });
4966
4967  
4968
4969  /*
4970  * - LGPL
4971  *
4972  * slider
4973  * 
4974  */
4975
4976
4977 /**
4978  * @class Roo.bootstrap.Slider
4979  * @extends Roo.bootstrap.Component
4980  * Bootstrap Slider class
4981  *    
4982  * @constructor
4983  * Create a new Slider
4984  * @param {Object} config The config object
4985  */
4986
4987 Roo.bootstrap.Slider = function(config){
4988     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4989 };
4990
4991 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4992     
4993     getAutoCreate : function(){
4994         
4995         var cfg = {
4996             tag: 'div',
4997             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4998             cn: [
4999                 {
5000                     tag: 'a',
5001                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5002                 }
5003             ]
5004         };
5005         
5006         return cfg;
5007     }
5008    
5009 });
5010
5011  /*
5012  * Based on:
5013  * Ext JS Library 1.1.1
5014  * Copyright(c) 2006-2007, Ext JS, LLC.
5015  *
5016  * Originally Released Under LGPL - original licence link has changed is not relivant.
5017  *
5018  * Fork - LGPL
5019  * <script type="text/javascript">
5020  */
5021  
5022
5023 /**
5024  * @class Roo.grid.ColumnModel
5025  * @extends Roo.util.Observable
5026  * This is the default implementation of a ColumnModel used by the Grid. It defines
5027  * the columns in the grid.
5028  * <br>Usage:<br>
5029  <pre><code>
5030  var colModel = new Roo.grid.ColumnModel([
5031         {header: "Ticker", width: 60, sortable: true, locked: true},
5032         {header: "Company Name", width: 150, sortable: true},
5033         {header: "Market Cap.", width: 100, sortable: true},
5034         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5035         {header: "Employees", width: 100, sortable: true, resizable: false}
5036  ]);
5037  </code></pre>
5038  * <p>
5039  
5040  * The config options listed for this class are options which may appear in each
5041  * individual column definition.
5042  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5043  * @constructor
5044  * @param {Object} config An Array of column config objects. See this class's
5045  * config objects for details.
5046 */
5047 Roo.grid.ColumnModel = function(config){
5048         /**
5049      * The config passed into the constructor
5050      */
5051     this.config = config;
5052     this.lookup = {};
5053
5054     // if no id, create one
5055     // if the column does not have a dataIndex mapping,
5056     // map it to the order it is in the config
5057     for(var i = 0, len = config.length; i < len; i++){
5058         var c = config[i];
5059         if(typeof c.dataIndex == "undefined"){
5060             c.dataIndex = i;
5061         }
5062         if(typeof c.renderer == "string"){
5063             c.renderer = Roo.util.Format[c.renderer];
5064         }
5065         if(typeof c.id == "undefined"){
5066             c.id = Roo.id();
5067         }
5068         if(c.editor && c.editor.xtype){
5069             c.editor  = Roo.factory(c.editor, Roo.grid);
5070         }
5071         if(c.editor && c.editor.isFormField){
5072             c.editor = new Roo.grid.GridEditor(c.editor);
5073         }
5074         this.lookup[c.id] = c;
5075     }
5076
5077     /**
5078      * The width of columns which have no width specified (defaults to 100)
5079      * @type Number
5080      */
5081     this.defaultWidth = 100;
5082
5083     /**
5084      * Default sortable of columns which have no sortable specified (defaults to false)
5085      * @type Boolean
5086      */
5087     this.defaultSortable = false;
5088
5089     this.addEvents({
5090         /**
5091              * @event widthchange
5092              * Fires when the width of a column changes.
5093              * @param {ColumnModel} this
5094              * @param {Number} columnIndex The column index
5095              * @param {Number} newWidth The new width
5096              */
5097             "widthchange": true,
5098         /**
5099              * @event headerchange
5100              * Fires when the text of a header changes.
5101              * @param {ColumnModel} this
5102              * @param {Number} columnIndex The column index
5103              * @param {Number} newText The new header text
5104              */
5105             "headerchange": true,
5106         /**
5107              * @event hiddenchange
5108              * Fires when a column is hidden or "unhidden".
5109              * @param {ColumnModel} this
5110              * @param {Number} columnIndex The column index
5111              * @param {Boolean} hidden true if hidden, false otherwise
5112              */
5113             "hiddenchange": true,
5114             /**
5115          * @event columnmoved
5116          * Fires when a column is moved.
5117          * @param {ColumnModel} this
5118          * @param {Number} oldIndex
5119          * @param {Number} newIndex
5120          */
5121         "columnmoved" : true,
5122         /**
5123          * @event columlockchange
5124          * Fires when a column's locked state is changed
5125          * @param {ColumnModel} this
5126          * @param {Number} colIndex
5127          * @param {Boolean} locked true if locked
5128          */
5129         "columnlockchange" : true
5130     });
5131     Roo.grid.ColumnModel.superclass.constructor.call(this);
5132 };
5133 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5134     /**
5135      * @cfg {String} header The header text to display in the Grid view.
5136      */
5137     /**
5138      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5139      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5140      * specified, the column's index is used as an index into the Record's data Array.
5141      */
5142     /**
5143      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5144      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5145      */
5146     /**
5147      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5148      * Defaults to the value of the {@link #defaultSortable} property.
5149      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5150      */
5151     /**
5152      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5153      */
5154     /**
5155      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5156      */
5157     /**
5158      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5159      */
5160     /**
5161      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5162      */
5163     /**
5164      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5165      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5166      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5167      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5168      */
5169        /**
5170      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5171      */
5172     /**
5173      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5174      */
5175     /**
5176      * @cfg {String} cursor (Optional)
5177      */
5178     /**
5179      * @cfg {String} tooltip (Optional)
5180      */
5181     /**
5182      * @cfg {Number} xs (Optional)
5183      */
5184     /**
5185      * @cfg {Number} sm (Optional)
5186      */
5187     /**
5188      * @cfg {Number} md (Optional)
5189      */
5190     /**
5191      * @cfg {Number} lg (Optional)
5192      */
5193     /**
5194      * Returns the id of the column at the specified index.
5195      * @param {Number} index The column index
5196      * @return {String} the id
5197      */
5198     getColumnId : function(index){
5199         return this.config[index].id;
5200     },
5201
5202     /**
5203      * Returns the column for a specified id.
5204      * @param {String} id The column id
5205      * @return {Object} the column
5206      */
5207     getColumnById : function(id){
5208         return this.lookup[id];
5209     },
5210
5211     
5212     /**
5213      * Returns the column for a specified dataIndex.
5214      * @param {String} dataIndex The column dataIndex
5215      * @return {Object|Boolean} the column or false if not found
5216      */
5217     getColumnByDataIndex: function(dataIndex){
5218         var index = this.findColumnIndex(dataIndex);
5219         return index > -1 ? this.config[index] : false;
5220     },
5221     
5222     /**
5223      * Returns the index for a specified column id.
5224      * @param {String} id The column id
5225      * @return {Number} the index, or -1 if not found
5226      */
5227     getIndexById : function(id){
5228         for(var i = 0, len = this.config.length; i < len; i++){
5229             if(this.config[i].id == id){
5230                 return i;
5231             }
5232         }
5233         return -1;
5234     },
5235     
5236     /**
5237      * Returns the index for a specified column dataIndex.
5238      * @param {String} dataIndex The column dataIndex
5239      * @return {Number} the index, or -1 if not found
5240      */
5241     
5242     findColumnIndex : function(dataIndex){
5243         for(var i = 0, len = this.config.length; i < len; i++){
5244             if(this.config[i].dataIndex == dataIndex){
5245                 return i;
5246             }
5247         }
5248         return -1;
5249     },
5250     
5251     
5252     moveColumn : function(oldIndex, newIndex){
5253         var c = this.config[oldIndex];
5254         this.config.splice(oldIndex, 1);
5255         this.config.splice(newIndex, 0, c);
5256         this.dataMap = null;
5257         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5258     },
5259
5260     isLocked : function(colIndex){
5261         return this.config[colIndex].locked === true;
5262     },
5263
5264     setLocked : function(colIndex, value, suppressEvent){
5265         if(this.isLocked(colIndex) == value){
5266             return;
5267         }
5268         this.config[colIndex].locked = value;
5269         if(!suppressEvent){
5270             this.fireEvent("columnlockchange", this, colIndex, value);
5271         }
5272     },
5273
5274     getTotalLockedWidth : function(){
5275         var totalWidth = 0;
5276         for(var i = 0; i < this.config.length; i++){
5277             if(this.isLocked(i) && !this.isHidden(i)){
5278                 this.totalWidth += this.getColumnWidth(i);
5279             }
5280         }
5281         return totalWidth;
5282     },
5283
5284     getLockedCount : function(){
5285         for(var i = 0, len = this.config.length; i < len; i++){
5286             if(!this.isLocked(i)){
5287                 return i;
5288             }
5289         }
5290         
5291         return this.config.length;
5292     },
5293
5294     /**
5295      * Returns the number of columns.
5296      * @return {Number}
5297      */
5298     getColumnCount : function(visibleOnly){
5299         if(visibleOnly === true){
5300             var c = 0;
5301             for(var i = 0, len = this.config.length; i < len; i++){
5302                 if(!this.isHidden(i)){
5303                     c++;
5304                 }
5305             }
5306             return c;
5307         }
5308         return this.config.length;
5309     },
5310
5311     /**
5312      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5313      * @param {Function} fn
5314      * @param {Object} scope (optional)
5315      * @return {Array} result
5316      */
5317     getColumnsBy : function(fn, scope){
5318         var r = [];
5319         for(var i = 0, len = this.config.length; i < len; i++){
5320             var c = this.config[i];
5321             if(fn.call(scope||this, c, i) === true){
5322                 r[r.length] = c;
5323             }
5324         }
5325         return r;
5326     },
5327
5328     /**
5329      * Returns true if the specified column is sortable.
5330      * @param {Number} col The column index
5331      * @return {Boolean}
5332      */
5333     isSortable : function(col){
5334         if(typeof this.config[col].sortable == "undefined"){
5335             return this.defaultSortable;
5336         }
5337         return this.config[col].sortable;
5338     },
5339
5340     /**
5341      * Returns the rendering (formatting) function defined for the column.
5342      * @param {Number} col The column index.
5343      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5344      */
5345     getRenderer : function(col){
5346         if(!this.config[col].renderer){
5347             return Roo.grid.ColumnModel.defaultRenderer;
5348         }
5349         return this.config[col].renderer;
5350     },
5351
5352     /**
5353      * Sets the rendering (formatting) function for a column.
5354      * @param {Number} col The column index
5355      * @param {Function} fn The function to use to process the cell's raw data
5356      * to return HTML markup for the grid view. The render function is called with
5357      * the following parameters:<ul>
5358      * <li>Data value.</li>
5359      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5360      * <li>css A CSS style string to apply to the table cell.</li>
5361      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5362      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5363      * <li>Row index</li>
5364      * <li>Column index</li>
5365      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5366      */
5367     setRenderer : function(col, fn){
5368         this.config[col].renderer = fn;
5369     },
5370
5371     /**
5372      * Returns the width for the specified column.
5373      * @param {Number} col The column index
5374      * @return {Number}
5375      */
5376     getColumnWidth : function(col){
5377         return this.config[col].width * 1 || this.defaultWidth;
5378     },
5379
5380     /**
5381      * Sets the width for a column.
5382      * @param {Number} col The column index
5383      * @param {Number} width The new width
5384      */
5385     setColumnWidth : function(col, width, suppressEvent){
5386         this.config[col].width = width;
5387         this.totalWidth = null;
5388         if(!suppressEvent){
5389              this.fireEvent("widthchange", this, col, width);
5390         }
5391     },
5392
5393     /**
5394      * Returns the total width of all columns.
5395      * @param {Boolean} includeHidden True to include hidden column widths
5396      * @return {Number}
5397      */
5398     getTotalWidth : function(includeHidden){
5399         if(!this.totalWidth){
5400             this.totalWidth = 0;
5401             for(var i = 0, len = this.config.length; i < len; i++){
5402                 if(includeHidden || !this.isHidden(i)){
5403                     this.totalWidth += this.getColumnWidth(i);
5404                 }
5405             }
5406         }
5407         return this.totalWidth;
5408     },
5409
5410     /**
5411      * Returns the header for the specified column.
5412      * @param {Number} col The column index
5413      * @return {String}
5414      */
5415     getColumnHeader : function(col){
5416         return this.config[col].header;
5417     },
5418
5419     /**
5420      * Sets the header for a column.
5421      * @param {Number} col The column index
5422      * @param {String} header The new header
5423      */
5424     setColumnHeader : function(col, header){
5425         this.config[col].header = header;
5426         this.fireEvent("headerchange", this, col, header);
5427     },
5428
5429     /**
5430      * Returns the tooltip for the specified column.
5431      * @param {Number} col The column index
5432      * @return {String}
5433      */
5434     getColumnTooltip : function(col){
5435             return this.config[col].tooltip;
5436     },
5437     /**
5438      * Sets the tooltip for a column.
5439      * @param {Number} col The column index
5440      * @param {String} tooltip The new tooltip
5441      */
5442     setColumnTooltip : function(col, tooltip){
5443             this.config[col].tooltip = tooltip;
5444     },
5445
5446     /**
5447      * Returns the dataIndex for the specified column.
5448      * @param {Number} col The column index
5449      * @return {Number}
5450      */
5451     getDataIndex : function(col){
5452         return this.config[col].dataIndex;
5453     },
5454
5455     /**
5456      * Sets the dataIndex for a column.
5457      * @param {Number} col The column index
5458      * @param {Number} dataIndex The new dataIndex
5459      */
5460     setDataIndex : function(col, dataIndex){
5461         this.config[col].dataIndex = dataIndex;
5462     },
5463
5464     
5465     
5466     /**
5467      * Returns true if the cell is editable.
5468      * @param {Number} colIndex The column index
5469      * @param {Number} rowIndex The row index - this is nto actually used..?
5470      * @return {Boolean}
5471      */
5472     isCellEditable : function(colIndex, rowIndex){
5473         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5474     },
5475
5476     /**
5477      * Returns the editor defined for the cell/column.
5478      * return false or null to disable editing.
5479      * @param {Number} colIndex The column index
5480      * @param {Number} rowIndex The row index
5481      * @return {Object}
5482      */
5483     getCellEditor : function(colIndex, rowIndex){
5484         return this.config[colIndex].editor;
5485     },
5486
5487     /**
5488      * Sets if a column is editable.
5489      * @param {Number} col The column index
5490      * @param {Boolean} editable True if the column is editable
5491      */
5492     setEditable : function(col, editable){
5493         this.config[col].editable = editable;
5494     },
5495
5496
5497     /**
5498      * Returns true if the column is hidden.
5499      * @param {Number} colIndex The column index
5500      * @return {Boolean}
5501      */
5502     isHidden : function(colIndex){
5503         return this.config[colIndex].hidden;
5504     },
5505
5506
5507     /**
5508      * Returns true if the column width cannot be changed
5509      */
5510     isFixed : function(colIndex){
5511         return this.config[colIndex].fixed;
5512     },
5513
5514     /**
5515      * Returns true if the column can be resized
5516      * @return {Boolean}
5517      */
5518     isResizable : function(colIndex){
5519         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5520     },
5521     /**
5522      * Sets if a column is hidden.
5523      * @param {Number} colIndex The column index
5524      * @param {Boolean} hidden True if the column is hidden
5525      */
5526     setHidden : function(colIndex, hidden){
5527         this.config[colIndex].hidden = hidden;
5528         this.totalWidth = null;
5529         this.fireEvent("hiddenchange", this, colIndex, hidden);
5530     },
5531
5532     /**
5533      * Sets the editor for a column.
5534      * @param {Number} col The column index
5535      * @param {Object} editor The editor object
5536      */
5537     setEditor : function(col, editor){
5538         this.config[col].editor = editor;
5539     }
5540 });
5541
5542 Roo.grid.ColumnModel.defaultRenderer = function(value){
5543         if(typeof value == "string" && value.length < 1){
5544             return "&#160;";
5545         }
5546         return value;
5547 };
5548
5549 // Alias for backwards compatibility
5550 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5551 /*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 /**
5563  * @class Roo.LoadMask
5564  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5565  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5566  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5567  * element's UpdateManager load indicator and will be destroyed after the initial load.
5568  * @constructor
5569  * Create a new LoadMask
5570  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5571  * @param {Object} config The config object
5572  */
5573 Roo.LoadMask = function(el, config){
5574     this.el = Roo.get(el);
5575     Roo.apply(this, config);
5576     if(this.store){
5577         this.store.on('beforeload', this.onBeforeLoad, this);
5578         this.store.on('load', this.onLoad, this);
5579         this.store.on('loadexception', this.onLoadException, this);
5580         this.removeMask = false;
5581     }else{
5582         var um = this.el.getUpdateManager();
5583         um.showLoadIndicator = false; // disable the default indicator
5584         um.on('beforeupdate', this.onBeforeLoad, this);
5585         um.on('update', this.onLoad, this);
5586         um.on('failure', this.onLoad, this);
5587         this.removeMask = true;
5588     }
5589 };
5590
5591 Roo.LoadMask.prototype = {
5592     /**
5593      * @cfg {Boolean} removeMask
5594      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5595      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5596      */
5597     /**
5598      * @cfg {String} msg
5599      * The text to display in a centered loading message box (defaults to 'Loading...')
5600      */
5601     msg : 'Loading...',
5602     /**
5603      * @cfg {String} msgCls
5604      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5605      */
5606     msgCls : 'x-mask-loading',
5607
5608     /**
5609      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5610      * @type Boolean
5611      */
5612     disabled: false,
5613
5614     /**
5615      * Disables the mask to prevent it from being displayed
5616      */
5617     disable : function(){
5618        this.disabled = true;
5619     },
5620
5621     /**
5622      * Enables the mask so that it can be displayed
5623      */
5624     enable : function(){
5625         this.disabled = false;
5626     },
5627     
5628     onLoadException : function()
5629     {
5630         Roo.log(arguments);
5631         
5632         if (typeof(arguments[3]) != 'undefined') {
5633             Roo.MessageBox.alert("Error loading",arguments[3]);
5634         } 
5635         /*
5636         try {
5637             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5638                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5639             }   
5640         } catch(e) {
5641             
5642         }
5643         */
5644     
5645         
5646         
5647         this.el.unmask(this.removeMask);
5648     },
5649     // private
5650     onLoad : function()
5651     {
5652         this.el.unmask(this.removeMask);
5653     },
5654
5655     // private
5656     onBeforeLoad : function(){
5657         if(!this.disabled){
5658             this.el.mask(this.msg, this.msgCls);
5659         }
5660     },
5661
5662     // private
5663     destroy : function(){
5664         if(this.store){
5665             this.store.un('beforeload', this.onBeforeLoad, this);
5666             this.store.un('load', this.onLoad, this);
5667             this.store.un('loadexception', this.onLoadException, this);
5668         }else{
5669             var um = this.el.getUpdateManager();
5670             um.un('beforeupdate', this.onBeforeLoad, this);
5671             um.un('update', this.onLoad, this);
5672             um.un('failure', this.onLoad, this);
5673         }
5674     }
5675 };/*
5676  * - LGPL
5677  *
5678  * table
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.Table
5684  * @extends Roo.bootstrap.Component
5685  * Bootstrap Table class
5686  * @cfg {String} cls table class
5687  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5688  * @cfg {String} bgcolor Specifies the background color for a table
5689  * @cfg {Number} border Specifies whether the table cells should have borders or not
5690  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5691  * @cfg {Number} cellspacing Specifies the space between cells
5692  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5693  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5694  * @cfg {String} sortable Specifies that the table should be sortable
5695  * @cfg {String} summary Specifies a summary of the content of a table
5696  * @cfg {Number} width Specifies the width of a table
5697  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5698  * 
5699  * @cfg {boolean} striped Should the rows be alternative striped
5700  * @cfg {boolean} bordered Add borders to the table
5701  * @cfg {boolean} hover Add hover highlighting
5702  * @cfg {boolean} condensed Format condensed
5703  * @cfg {boolean} responsive Format condensed
5704  * @cfg {Boolean} loadMask (true|false) default false
5705  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5706  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5707  * @cfg {Boolean} rowSelection (true|false) default false
5708  * @cfg {Boolean} cellSelection (true|false) default false
5709  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5710  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5711  
5712  * 
5713  * @constructor
5714  * Create a new Table
5715  * @param {Object} config The config object
5716  */
5717
5718 Roo.bootstrap.Table = function(config){
5719     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5720     
5721   
5722     
5723     // BC...
5724     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5725     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5726     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5727     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5728     
5729     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5730     if (this.sm) {
5731         this.sm.grid = this;
5732         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5733         this.sm = this.selModel;
5734         this.sm.xmodule = this.xmodule || false;
5735     }
5736     
5737     if (this.cm && typeof(this.cm.config) == 'undefined') {
5738         this.colModel = new Roo.grid.ColumnModel(this.cm);
5739         this.cm = this.colModel;
5740         this.cm.xmodule = this.xmodule || false;
5741     }
5742     if (this.store) {
5743         this.store= Roo.factory(this.store, Roo.data);
5744         this.ds = this.store;
5745         this.ds.xmodule = this.xmodule || false;
5746          
5747     }
5748     if (this.footer && this.store) {
5749         this.footer.dataSource = this.ds;
5750         this.footer = Roo.factory(this.footer);
5751     }
5752     
5753     /** @private */
5754     this.addEvents({
5755         /**
5756          * @event cellclick
5757          * Fires when a cell is clicked
5758          * @param {Roo.bootstrap.Table} this
5759          * @param {Roo.Element} el
5760          * @param {Number} rowIndex
5761          * @param {Number} columnIndex
5762          * @param {Roo.EventObject} e
5763          */
5764         "cellclick" : true,
5765         /**
5766          * @event celldblclick
5767          * Fires when a cell is double clicked
5768          * @param {Roo.bootstrap.Table} this
5769          * @param {Roo.Element} el
5770          * @param {Number} rowIndex
5771          * @param {Number} columnIndex
5772          * @param {Roo.EventObject} e
5773          */
5774         "celldblclick" : true,
5775         /**
5776          * @event rowclick
5777          * Fires when a row is clicked
5778          * @param {Roo.bootstrap.Table} this
5779          * @param {Roo.Element} el
5780          * @param {Number} rowIndex
5781          * @param {Roo.EventObject} e
5782          */
5783         "rowclick" : true,
5784         /**
5785          * @event rowdblclick
5786          * Fires when a row is double clicked
5787          * @param {Roo.bootstrap.Table} this
5788          * @param {Roo.Element} el
5789          * @param {Number} rowIndex
5790          * @param {Roo.EventObject} e
5791          */
5792         "rowdblclick" : true,
5793         /**
5794          * @event mouseover
5795          * Fires when a mouseover occur
5796          * @param {Roo.bootstrap.Table} this
5797          * @param {Roo.Element} el
5798          * @param {Number} rowIndex
5799          * @param {Number} columnIndex
5800          * @param {Roo.EventObject} e
5801          */
5802         "mouseover" : true,
5803         /**
5804          * @event mouseout
5805          * Fires when a mouseout occur
5806          * @param {Roo.bootstrap.Table} this
5807          * @param {Roo.Element} el
5808          * @param {Number} rowIndex
5809          * @param {Number} columnIndex
5810          * @param {Roo.EventObject} e
5811          */
5812         "mouseout" : true,
5813         /**
5814          * @event rowclass
5815          * Fires when a row is rendered, so you can change add a style to it.
5816          * @param {Roo.bootstrap.Table} this
5817          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5818          */
5819         'rowclass' : true,
5820           /**
5821          * @event rowsrendered
5822          * Fires when all the  rows have been rendered
5823          * @param {Roo.bootstrap.Table} this
5824          */
5825         'rowsrendered' : true,
5826         /**
5827          * @event contextmenu
5828          * The raw contextmenu event for the entire grid.
5829          * @param {Roo.EventObject} e
5830          */
5831         "contextmenu" : true,
5832         /**
5833          * @event rowcontextmenu
5834          * Fires when a row is right clicked
5835          * @param {Roo.bootstrap.Table} this
5836          * @param {Number} rowIndex
5837          * @param {Roo.EventObject} e
5838          */
5839         "rowcontextmenu" : true,
5840         /**
5841          * @event cellcontextmenu
5842          * Fires when a cell is right clicked
5843          * @param {Roo.bootstrap.Table} this
5844          * @param {Number} rowIndex
5845          * @param {Number} cellIndex
5846          * @param {Roo.EventObject} e
5847          */
5848          "cellcontextmenu" : true,
5849          /**
5850          * @event headercontextmenu
5851          * Fires when a header is right clicked
5852          * @param {Roo.bootstrap.Table} this
5853          * @param {Number} columnIndex
5854          * @param {Roo.EventObject} e
5855          */
5856         "headercontextmenu" : true
5857     });
5858 };
5859
5860 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5861     
5862     cls: false,
5863     align: false,
5864     bgcolor: false,
5865     border: false,
5866     cellpadding: false,
5867     cellspacing: false,
5868     frame: false,
5869     rules: false,
5870     sortable: false,
5871     summary: false,
5872     width: false,
5873     striped : false,
5874     scrollBody : false,
5875     bordered: false,
5876     hover:  false,
5877     condensed : false,
5878     responsive : false,
5879     sm : false,
5880     cm : false,
5881     store : false,
5882     loadMask : false,
5883     footerShow : true,
5884     headerShow : true,
5885   
5886     rowSelection : false,
5887     cellSelection : false,
5888     layout : false,
5889     
5890     // Roo.Element - the tbody
5891     mainBody: false,
5892     // Roo.Element - thead element
5893     mainHead: false,
5894     
5895     container: false, // used by gridpanel...
5896     
5897     getAutoCreate : function()
5898     {
5899         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5900         
5901         cfg = {
5902             tag: 'table',
5903             cls : 'table',
5904             cn : []
5905         };
5906         if (this.scrollBody) {
5907             cfg.cls += ' table-body-fixed';
5908         }    
5909         if (this.striped) {
5910             cfg.cls += ' table-striped';
5911         }
5912         
5913         if (this.hover) {
5914             cfg.cls += ' table-hover';
5915         }
5916         if (this.bordered) {
5917             cfg.cls += ' table-bordered';
5918         }
5919         if (this.condensed) {
5920             cfg.cls += ' table-condensed';
5921         }
5922         if (this.responsive) {
5923             cfg.cls += ' table-responsive';
5924         }
5925         
5926         if (this.cls) {
5927             cfg.cls+=  ' ' +this.cls;
5928         }
5929         
5930         // this lot should be simplifed...
5931         
5932         if (this.align) {
5933             cfg.align=this.align;
5934         }
5935         if (this.bgcolor) {
5936             cfg.bgcolor=this.bgcolor;
5937         }
5938         if (this.border) {
5939             cfg.border=this.border;
5940         }
5941         if (this.cellpadding) {
5942             cfg.cellpadding=this.cellpadding;
5943         }
5944         if (this.cellspacing) {
5945             cfg.cellspacing=this.cellspacing;
5946         }
5947         if (this.frame) {
5948             cfg.frame=this.frame;
5949         }
5950         if (this.rules) {
5951             cfg.rules=this.rules;
5952         }
5953         if (this.sortable) {
5954             cfg.sortable=this.sortable;
5955         }
5956         if (this.summary) {
5957             cfg.summary=this.summary;
5958         }
5959         if (this.width) {
5960             cfg.width=this.width;
5961         }
5962         if (this.layout) {
5963             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5964         }
5965         
5966         if(this.store || this.cm){
5967             if(this.headerShow){
5968                 cfg.cn.push(this.renderHeader());
5969             }
5970             
5971             cfg.cn.push(this.renderBody());
5972             
5973             if(this.footerShow){
5974                 cfg.cn.push(this.renderFooter());
5975             }
5976             // where does this come from?
5977             //cfg.cls+=  ' TableGrid';
5978         }
5979         
5980         return { cn : [ cfg ] };
5981     },
5982     
5983     initEvents : function()
5984     {   
5985         if(!this.store || !this.cm){
5986             return;
5987         }
5988         if (this.selModel) {
5989             this.selModel.initEvents();
5990         }
5991         
5992         
5993         //Roo.log('initEvents with ds!!!!');
5994         
5995         this.mainBody = this.el.select('tbody', true).first();
5996         this.mainHead = this.el.select('thead', true).first();
5997         
5998         
5999         
6000         
6001         var _this = this;
6002         
6003         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6004             e.on('click', _this.sort, _this);
6005         });
6006         
6007         this.el.on("click", this.onClick, this);
6008         this.el.on("dblclick", this.onDblClick, this);
6009         
6010         // why is this done????? = it breaks dialogs??
6011         //this.parent().el.setStyle('position', 'relative');
6012         
6013         
6014         if (this.footer) {
6015             this.footer.parentId = this.id;
6016             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6017         }
6018         
6019         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6020         
6021         this.store.on('load', this.onLoad, this);
6022         this.store.on('beforeload', this.onBeforeLoad, this);
6023         this.store.on('update', this.onUpdate, this);
6024         this.store.on('add', this.onAdd, this);
6025         this.store.on("clear", this.clear, this);
6026         
6027         this.el.on("contextmenu", this.onContextMenu, this);
6028         
6029         this.mainBody.on('scroll', this.onBodyScroll, this);
6030         
6031         
6032     },
6033     
6034     onContextMenu : function(e, t)
6035     {
6036         this.processEvent("contextmenu", e);
6037     },
6038     
6039     processEvent : function(name, e)
6040     {
6041         if (name != 'touchstart' ) {
6042             this.fireEvent(name, e);    
6043         }
6044         
6045         var t = e.getTarget();
6046         
6047         var cell = Roo.get(t);
6048         
6049         if(!cell){
6050             return;
6051         }
6052         
6053         if(cell.findParent('tfoot', false, true)){
6054             return;
6055         }
6056         
6057         if(cell.findParent('thead', false, true)){
6058             
6059             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6060                 cell = Roo.get(t).findParent('th', false, true);
6061                 if (!cell) {
6062                     Roo.log("failed to find th in thead?");
6063                     Roo.log(e.getTarget());
6064                     return;
6065                 }
6066             }
6067             
6068             var cellIndex = cell.dom.cellIndex;
6069             
6070             var ename = name == 'touchstart' ? 'click' : name;
6071             this.fireEvent("header" + ename, this, cellIndex, e);
6072             
6073             return;
6074         }
6075         
6076         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6077             cell = Roo.get(t).findParent('td', false, true);
6078             if (!cell) {
6079                 Roo.log("failed to find th in tbody?");
6080                 Roo.log(e.getTarget());
6081                 return;
6082             }
6083         }
6084         
6085         var row = cell.findParent('tr', false, true);
6086         var cellIndex = cell.dom.cellIndex;
6087         var rowIndex = row.dom.rowIndex - 1;
6088         
6089         if(row !== false){
6090             
6091             this.fireEvent("row" + name, this, rowIndex, e);
6092             
6093             if(cell !== false){
6094             
6095                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6096             }
6097         }
6098         
6099     },
6100     
6101     onMouseover : function(e, el)
6102     {
6103         var cell = Roo.get(el);
6104         
6105         if(!cell){
6106             return;
6107         }
6108         
6109         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110             cell = cell.findParent('td', false, true);
6111         }
6112         
6113         var row = cell.findParent('tr', false, true);
6114         var cellIndex = cell.dom.cellIndex;
6115         var rowIndex = row.dom.rowIndex - 1; // start from 0
6116         
6117         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6118         
6119     },
6120     
6121     onMouseout : function(e, el)
6122     {
6123         var cell = Roo.get(el);
6124         
6125         if(!cell){
6126             return;
6127         }
6128         
6129         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130             cell = cell.findParent('td', false, true);
6131         }
6132         
6133         var row = cell.findParent('tr', false, true);
6134         var cellIndex = cell.dom.cellIndex;
6135         var rowIndex = row.dom.rowIndex - 1; // start from 0
6136         
6137         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6138         
6139     },
6140     
6141     onClick : function(e, el)
6142     {
6143         var cell = Roo.get(el);
6144         
6145         if(!cell || (!this.cellSelection && !this.rowSelection)){
6146             return;
6147         }
6148         
6149         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6150             cell = cell.findParent('td', false, true);
6151         }
6152         
6153         if(!cell || typeof(cell) == 'undefined'){
6154             return;
6155         }
6156         
6157         var row = cell.findParent('tr', false, true);
6158         
6159         if(!row || typeof(row) == 'undefined'){
6160             return;
6161         }
6162         
6163         var cellIndex = cell.dom.cellIndex;
6164         var rowIndex = this.getRowIndex(row);
6165         
6166         // why??? - should these not be based on SelectionModel?
6167         if(this.cellSelection){
6168             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6169         }
6170         
6171         if(this.rowSelection){
6172             this.fireEvent('rowclick', this, row, rowIndex, e);
6173         }
6174         
6175         
6176     },
6177         
6178     onDblClick : function(e,el)
6179     {
6180         var cell = Roo.get(el);
6181         
6182         if(!cell || (!this.cellSelection && !this.rowSelection)){
6183             return;
6184         }
6185         
6186         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6187             cell = cell.findParent('td', false, true);
6188         }
6189         
6190         if(!cell || typeof(cell) == 'undefined'){
6191             return;
6192         }
6193         
6194         var row = cell.findParent('tr', false, true);
6195         
6196         if(!row || typeof(row) == 'undefined'){
6197             return;
6198         }
6199         
6200         var cellIndex = cell.dom.cellIndex;
6201         var rowIndex = this.getRowIndex(row);
6202         
6203         if(this.cellSelection){
6204             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6205         }
6206         
6207         if(this.rowSelection){
6208             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6209         }
6210     },
6211     
6212     sort : function(e,el)
6213     {
6214         var col = Roo.get(el);
6215         
6216         if(!col.hasClass('sortable')){
6217             return;
6218         }
6219         
6220         var sort = col.attr('sort');
6221         var dir = 'ASC';
6222         
6223         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6224             dir = 'DESC';
6225         }
6226         
6227         this.store.sortInfo = {field : sort, direction : dir};
6228         
6229         if (this.footer) {
6230             Roo.log("calling footer first");
6231             this.footer.onClick('first');
6232         } else {
6233         
6234             this.store.load({ params : { start : 0 } });
6235         }
6236     },
6237     
6238     renderHeader : function()
6239     {
6240         var header = {
6241             tag: 'thead',
6242             cn : []
6243         };
6244         
6245         var cm = this.cm;
6246         this.totalWidth = 0;
6247         
6248         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6249             
6250             var config = cm.config[i];
6251             
6252             var c = {
6253                 tag: 'th',
6254                 style : '',
6255                 html: cm.getColumnHeader(i)
6256             };
6257             
6258             var hh = '';
6259             
6260             if(typeof(config.sortable) != 'undefined' && config.sortable){
6261                 c.cls = 'sortable';
6262                 c.html = '<i class="glyphicon"></i>' + c.html;
6263             }
6264             
6265             if(typeof(config.lgHeader) != 'undefined'){
6266                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6267             }
6268             
6269             if(typeof(config.mdHeader) != 'undefined'){
6270                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6271             }
6272             
6273             if(typeof(config.smHeader) != 'undefined'){
6274                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6275             }
6276             
6277             if(typeof(config.xsHeader) != 'undefined'){
6278                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6279             }
6280             
6281             if(hh.length){
6282                 c.html = hh;
6283             }
6284             
6285             if(typeof(config.tooltip) != 'undefined'){
6286                 c.tooltip = config.tooltip;
6287             }
6288             
6289             if(typeof(config.colspan) != 'undefined'){
6290                 c.colspan = config.colspan;
6291             }
6292             
6293             if(typeof(config.hidden) != 'undefined' && config.hidden){
6294                 c.style += ' display:none;';
6295             }
6296             
6297             if(typeof(config.dataIndex) != 'undefined'){
6298                 c.sort = config.dataIndex;
6299             }
6300             
6301            
6302             
6303             if(typeof(config.align) != 'undefined' && config.align.length){
6304                 c.style += ' text-align:' + config.align + ';';
6305             }
6306             
6307             if(typeof(config.width) != 'undefined'){
6308                 c.style += ' width:' + config.width + 'px;';
6309                 this.totalWidth += config.width;
6310             } else {
6311                 this.totalWidth += 100; // assume minimum of 100 per column?
6312             }
6313             
6314             if(typeof(config.cls) != 'undefined'){
6315                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6316             }
6317             
6318             ['xs','sm','md','lg'].map(function(size){
6319                 
6320                 if(typeof(config[size]) == 'undefined'){
6321                     return;
6322                 }
6323                 
6324                 if (!config[size]) { // 0 = hidden
6325                     c.cls += ' hidden-' + size;
6326                     return;
6327                 }
6328                 
6329                 c.cls += ' col-' + size + '-' + config[size];
6330
6331             });
6332             
6333             header.cn.push(c)
6334         }
6335         
6336         return header;
6337     },
6338     
6339     renderBody : function()
6340     {
6341         var body = {
6342             tag: 'tbody',
6343             cn : [
6344                 {
6345                     tag: 'tr',
6346                     cn : [
6347                         {
6348                             tag : 'td',
6349                             colspan :  this.cm.getColumnCount()
6350                         }
6351                     ]
6352                 }
6353             ]
6354         };
6355         
6356         return body;
6357     },
6358     
6359     renderFooter : function()
6360     {
6361         var footer = {
6362             tag: 'tfoot',
6363             cn : [
6364                 {
6365                     tag: 'tr',
6366                     cn : [
6367                         {
6368                             tag : 'td',
6369                             colspan :  this.cm.getColumnCount()
6370                         }
6371                     ]
6372                 }
6373             ]
6374         };
6375         
6376         return footer;
6377     },
6378     
6379     
6380     
6381     onLoad : function()
6382     {
6383 //        Roo.log('ds onload');
6384         this.clear();
6385         
6386         var _this = this;
6387         var cm = this.cm;
6388         var ds = this.store;
6389         
6390         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6391             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6392             if (_this.store.sortInfo) {
6393                     
6394                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6395                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6396                 }
6397                 
6398                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6399                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6400                 }
6401             }
6402         });
6403         
6404         var tbody =  this.mainBody;
6405               
6406         if(ds.getCount() > 0){
6407             ds.data.each(function(d,rowIndex){
6408                 var row =  this.renderRow(cm, ds, rowIndex);
6409                 
6410                 tbody.createChild(row);
6411                 
6412                 var _this = this;
6413                 
6414                 if(row.cellObjects.length){
6415                     Roo.each(row.cellObjects, function(r){
6416                         _this.renderCellObject(r);
6417                     })
6418                 }
6419                 
6420             }, this);
6421         }
6422         
6423         Roo.each(this.el.select('tbody td', true).elements, function(e){
6424             e.on('mouseover', _this.onMouseover, _this);
6425         });
6426         
6427         Roo.each(this.el.select('tbody td', true).elements, function(e){
6428             e.on('mouseout', _this.onMouseout, _this);
6429         });
6430         this.fireEvent('rowsrendered', this);
6431         //if(this.loadMask){
6432         //    this.maskEl.hide();
6433         //}
6434         
6435         this.autoSize();
6436     },
6437     
6438     
6439     onUpdate : function(ds,record)
6440     {
6441         this.refreshRow(record);
6442         this.autoSize();
6443     },
6444     
6445     onRemove : function(ds, record, index, isUpdate){
6446         if(isUpdate !== true){
6447             this.fireEvent("beforerowremoved", this, index, record);
6448         }
6449         var bt = this.mainBody.dom;
6450         
6451         var rows = this.el.select('tbody > tr', true).elements;
6452         
6453         if(typeof(rows[index]) != 'undefined'){
6454             bt.removeChild(rows[index].dom);
6455         }
6456         
6457 //        if(bt.rows[index]){
6458 //            bt.removeChild(bt.rows[index]);
6459 //        }
6460         
6461         if(isUpdate !== true){
6462             //this.stripeRows(index);
6463             //this.syncRowHeights(index, index);
6464             //this.layout();
6465             this.fireEvent("rowremoved", this, index, record);
6466         }
6467     },
6468     
6469     onAdd : function(ds, records, rowIndex)
6470     {
6471         //Roo.log('on Add called');
6472         // - note this does not handle multiple adding very well..
6473         var bt = this.mainBody.dom;
6474         for (var i =0 ; i < records.length;i++) {
6475             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6476             //Roo.log(records[i]);
6477             //Roo.log(this.store.getAt(rowIndex+i));
6478             this.insertRow(this.store, rowIndex + i, false);
6479             return;
6480         }
6481         
6482     },
6483     
6484     
6485     refreshRow : function(record){
6486         var ds = this.store, index;
6487         if(typeof record == 'number'){
6488             index = record;
6489             record = ds.getAt(index);
6490         }else{
6491             index = ds.indexOf(record);
6492         }
6493         this.insertRow(ds, index, true);
6494         this.autoSize();
6495         this.onRemove(ds, record, index+1, true);
6496         this.autoSize();
6497         //this.syncRowHeights(index, index);
6498         //this.layout();
6499         this.fireEvent("rowupdated", this, index, record);
6500     },
6501     
6502     insertRow : function(dm, rowIndex, isUpdate){
6503         
6504         if(!isUpdate){
6505             this.fireEvent("beforerowsinserted", this, rowIndex);
6506         }
6507             //var s = this.getScrollState();
6508         var row = this.renderRow(this.cm, this.store, rowIndex);
6509         // insert before rowIndex..
6510         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6511         
6512         var _this = this;
6513                 
6514         if(row.cellObjects.length){
6515             Roo.each(row.cellObjects, function(r){
6516                 _this.renderCellObject(r);
6517             })
6518         }
6519             
6520         if(!isUpdate){
6521             this.fireEvent("rowsinserted", this, rowIndex);
6522             //this.syncRowHeights(firstRow, lastRow);
6523             //this.stripeRows(firstRow);
6524             //this.layout();
6525         }
6526         
6527     },
6528     
6529     
6530     getRowDom : function(rowIndex)
6531     {
6532         var rows = this.el.select('tbody > tr', true).elements;
6533         
6534         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6535         
6536     },
6537     // returns the object tree for a tr..
6538   
6539     
6540     renderRow : function(cm, ds, rowIndex) 
6541     {
6542         
6543         var d = ds.getAt(rowIndex);
6544         
6545         var row = {
6546             tag : 'tr',
6547             cn : []
6548         };
6549             
6550         var cellObjects = [];
6551         
6552         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6553             var config = cm.config[i];
6554             
6555             var renderer = cm.getRenderer(i);
6556             var value = '';
6557             var id = false;
6558             
6559             if(typeof(renderer) !== 'undefined'){
6560                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6561             }
6562             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6563             // and are rendered into the cells after the row is rendered - using the id for the element.
6564             
6565             if(typeof(value) === 'object'){
6566                 id = Roo.id();
6567                 cellObjects.push({
6568                     container : id,
6569                     cfg : value 
6570                 })
6571             }
6572             
6573             var rowcfg = {
6574                 record: d,
6575                 rowIndex : rowIndex,
6576                 colIndex : i,
6577                 rowClass : ''
6578             };
6579
6580             this.fireEvent('rowclass', this, rowcfg);
6581             
6582             var td = {
6583                 tag: 'td',
6584                 cls : rowcfg.rowClass,
6585                 style: '',
6586                 html: (typeof(value) === 'object') ? '' : value
6587             };
6588             
6589             if (id) {
6590                 td.id = id;
6591             }
6592             
6593             if(typeof(config.colspan) != 'undefined'){
6594                 td.colspan = config.colspan;
6595             }
6596             
6597             if(typeof(config.hidden) != 'undefined' && config.hidden){
6598                 td.style += ' display:none;';
6599             }
6600             
6601             if(typeof(config.align) != 'undefined' && config.align.length){
6602                 td.style += ' text-align:' + config.align + ';';
6603             }
6604             
6605             if(typeof(config.width) != 'undefined'){
6606                 td.style += ' width:' +  config.width + 'px;';
6607             }
6608             
6609             if(typeof(config.cursor) != 'undefined'){
6610                 td.style += ' cursor:' +  config.cursor + ';';
6611             }
6612             
6613             if(typeof(config.cls) != 'undefined'){
6614                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6615             }
6616             
6617             ['xs','sm','md','lg'].map(function(size){
6618                 
6619                 if(typeof(config[size]) == 'undefined'){
6620                     return;
6621                 }
6622                 
6623                 if (!config[size]) { // 0 = hidden
6624                     td.cls += ' hidden-' + size;
6625                     return;
6626                 }
6627                 
6628                 td.cls += ' col-' + size + '-' + config[size];
6629
6630             });
6631              
6632             row.cn.push(td);
6633            
6634         }
6635         
6636         row.cellObjects = cellObjects;
6637         
6638         return row;
6639           
6640     },
6641     
6642     
6643     
6644     onBeforeLoad : function()
6645     {
6646         //Roo.log('ds onBeforeLoad');
6647         
6648         //this.clear();
6649         
6650         //if(this.loadMask){
6651         //    this.maskEl.show();
6652         //}
6653     },
6654      /**
6655      * Remove all rows
6656      */
6657     clear : function()
6658     {
6659         this.el.select('tbody', true).first().dom.innerHTML = '';
6660     },
6661     /**
6662      * Show or hide a row.
6663      * @param {Number} rowIndex to show or hide
6664      * @param {Boolean} state hide
6665      */
6666     setRowVisibility : function(rowIndex, state)
6667     {
6668         var bt = this.mainBody.dom;
6669         
6670         var rows = this.el.select('tbody > tr', true).elements;
6671         
6672         if(typeof(rows[rowIndex]) == 'undefined'){
6673             return;
6674         }
6675         rows[rowIndex].dom.style.display = state ? '' : 'none';
6676     },
6677     
6678     
6679     getSelectionModel : function(){
6680         if(!this.selModel){
6681             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6682         }
6683         return this.selModel;
6684     },
6685     /*
6686      * Render the Roo.bootstrap object from renderder
6687      */
6688     renderCellObject : function(r)
6689     {
6690         var _this = this;
6691         
6692         var t = r.cfg.render(r.container);
6693         
6694         if(r.cfg.cn){
6695             Roo.each(r.cfg.cn, function(c){
6696                 var child = {
6697                     container: t.getChildContainer(),
6698                     cfg: c
6699                 };
6700                 _this.renderCellObject(child);
6701             })
6702         }
6703     },
6704     
6705     getRowIndex : function(row)
6706     {
6707         var rowIndex = -1;
6708         
6709         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6710             if(el != row){
6711                 return;
6712             }
6713             
6714             rowIndex = index;
6715         });
6716         
6717         return rowIndex;
6718     },
6719      /**
6720      * Returns the grid's underlying element = used by panel.Grid
6721      * @return {Element} The element
6722      */
6723     getGridEl : function(){
6724         return this.el;
6725     },
6726      /**
6727      * Forces a resize - used by panel.Grid
6728      * @return {Element} The element
6729      */
6730     autoSize : function()
6731     {
6732         //var ctr = Roo.get(this.container.dom.parentElement);
6733         var ctr = Roo.get(this.el.dom);
6734         
6735         var thd = this.getGridEl().select('thead',true).first();
6736         var tbd = this.getGridEl().select('tbody', true).first();
6737         
6738         
6739         var cw = ctr.getWidth();
6740         
6741         if (tbd) {
6742             
6743             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6744             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6745             cw -= barsize;
6746         }
6747         cw = Math.max(cw, this.totalWidth);
6748         this.getGridEl().select('tr',true).setWidth(cw);
6749         // resize 'expandable coloumn?
6750         
6751         return; // we doe not have a view in this design..
6752         
6753     },
6754     onBodyScroll: function()
6755     {
6756         
6757         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6758         this.mainHead.setStyle({
6759                     'position' : 'relative',
6760                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6761         });
6762         
6763         
6764     }
6765 });
6766
6767  
6768
6769  /*
6770  * - LGPL
6771  *
6772  * table cell
6773  * 
6774  */
6775
6776 /**
6777  * @class Roo.bootstrap.TableCell
6778  * @extends Roo.bootstrap.Component
6779  * Bootstrap TableCell class
6780  * @cfg {String} html cell contain text
6781  * @cfg {String} cls cell class
6782  * @cfg {String} tag cell tag (td|th) default td
6783  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6784  * @cfg {String} align Aligns the content in a cell
6785  * @cfg {String} axis Categorizes cells
6786  * @cfg {String} bgcolor Specifies the background color of a cell
6787  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6788  * @cfg {Number} colspan Specifies the number of columns a cell should span
6789  * @cfg {String} headers Specifies one or more header cells a cell is related to
6790  * @cfg {Number} height Sets the height of a cell
6791  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6792  * @cfg {Number} rowspan Sets the number of rows a cell should span
6793  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6794  * @cfg {String} valign Vertical aligns the content in a cell
6795  * @cfg {Number} width Specifies the width of a cell
6796  * 
6797  * @constructor
6798  * Create a new TableCell
6799  * @param {Object} config The config object
6800  */
6801
6802 Roo.bootstrap.TableCell = function(config){
6803     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6804 };
6805
6806 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6807     
6808     html: false,
6809     cls: false,
6810     tag: false,
6811     abbr: false,
6812     align: false,
6813     axis: false,
6814     bgcolor: false,
6815     charoff: false,
6816     colspan: false,
6817     headers: false,
6818     height: false,
6819     nowrap: false,
6820     rowspan: false,
6821     scope: false,
6822     valign: false,
6823     width: false,
6824     
6825     
6826     getAutoCreate : function(){
6827         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6828         
6829         cfg = {
6830             tag: 'td'
6831         };
6832         
6833         if(this.tag){
6834             cfg.tag = this.tag;
6835         }
6836         
6837         if (this.html) {
6838             cfg.html=this.html
6839         }
6840         if (this.cls) {
6841             cfg.cls=this.cls
6842         }
6843         if (this.abbr) {
6844             cfg.abbr=this.abbr
6845         }
6846         if (this.align) {
6847             cfg.align=this.align
6848         }
6849         if (this.axis) {
6850             cfg.axis=this.axis
6851         }
6852         if (this.bgcolor) {
6853             cfg.bgcolor=this.bgcolor
6854         }
6855         if (this.charoff) {
6856             cfg.charoff=this.charoff
6857         }
6858         if (this.colspan) {
6859             cfg.colspan=this.colspan
6860         }
6861         if (this.headers) {
6862             cfg.headers=this.headers
6863         }
6864         if (this.height) {
6865             cfg.height=this.height
6866         }
6867         if (this.nowrap) {
6868             cfg.nowrap=this.nowrap
6869         }
6870         if (this.rowspan) {
6871             cfg.rowspan=this.rowspan
6872         }
6873         if (this.scope) {
6874             cfg.scope=this.scope
6875         }
6876         if (this.valign) {
6877             cfg.valign=this.valign
6878         }
6879         if (this.width) {
6880             cfg.width=this.width
6881         }
6882         
6883         
6884         return cfg;
6885     }
6886    
6887 });
6888
6889  
6890
6891  /*
6892  * - LGPL
6893  *
6894  * table row
6895  * 
6896  */
6897
6898 /**
6899  * @class Roo.bootstrap.TableRow
6900  * @extends Roo.bootstrap.Component
6901  * Bootstrap TableRow class
6902  * @cfg {String} cls row class
6903  * @cfg {String} align Aligns the content in a table row
6904  * @cfg {String} bgcolor Specifies a background color for a table row
6905  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6906  * @cfg {String} valign Vertical aligns the content in a table row
6907  * 
6908  * @constructor
6909  * Create a new TableRow
6910  * @param {Object} config The config object
6911  */
6912
6913 Roo.bootstrap.TableRow = function(config){
6914     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6915 };
6916
6917 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6918     
6919     cls: false,
6920     align: false,
6921     bgcolor: false,
6922     charoff: false,
6923     valign: false,
6924     
6925     getAutoCreate : function(){
6926         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6927         
6928         cfg = {
6929             tag: 'tr'
6930         };
6931             
6932         if(this.cls){
6933             cfg.cls = this.cls;
6934         }
6935         if(this.align){
6936             cfg.align = this.align;
6937         }
6938         if(this.bgcolor){
6939             cfg.bgcolor = this.bgcolor;
6940         }
6941         if(this.charoff){
6942             cfg.charoff = this.charoff;
6943         }
6944         if(this.valign){
6945             cfg.valign = this.valign;
6946         }
6947         
6948         return cfg;
6949     }
6950    
6951 });
6952
6953  
6954
6955  /*
6956  * - LGPL
6957  *
6958  * table body
6959  * 
6960  */
6961
6962 /**
6963  * @class Roo.bootstrap.TableBody
6964  * @extends Roo.bootstrap.Component
6965  * Bootstrap TableBody class
6966  * @cfg {String} cls element class
6967  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6968  * @cfg {String} align Aligns the content inside the element
6969  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6970  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6971  * 
6972  * @constructor
6973  * Create a new TableBody
6974  * @param {Object} config The config object
6975  */
6976
6977 Roo.bootstrap.TableBody = function(config){
6978     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6979 };
6980
6981 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6982     
6983     cls: false,
6984     tag: false,
6985     align: false,
6986     charoff: false,
6987     valign: false,
6988     
6989     getAutoCreate : function(){
6990         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6991         
6992         cfg = {
6993             tag: 'tbody'
6994         };
6995             
6996         if (this.cls) {
6997             cfg.cls=this.cls
6998         }
6999         if(this.tag){
7000             cfg.tag = this.tag;
7001         }
7002         
7003         if(this.align){
7004             cfg.align = this.align;
7005         }
7006         if(this.charoff){
7007             cfg.charoff = this.charoff;
7008         }
7009         if(this.valign){
7010             cfg.valign = this.valign;
7011         }
7012         
7013         return cfg;
7014     }
7015     
7016     
7017 //    initEvents : function()
7018 //    {
7019 //        
7020 //        if(!this.store){
7021 //            return;
7022 //        }
7023 //        
7024 //        this.store = Roo.factory(this.store, Roo.data);
7025 //        this.store.on('load', this.onLoad, this);
7026 //        
7027 //        this.store.load();
7028 //        
7029 //    },
7030 //    
7031 //    onLoad: function () 
7032 //    {   
7033 //        this.fireEvent('load', this);
7034 //    }
7035 //    
7036 //   
7037 });
7038
7039  
7040
7041  /*
7042  * Based on:
7043  * Ext JS Library 1.1.1
7044  * Copyright(c) 2006-2007, Ext JS, LLC.
7045  *
7046  * Originally Released Under LGPL - original licence link has changed is not relivant.
7047  *
7048  * Fork - LGPL
7049  * <script type="text/javascript">
7050  */
7051
7052 // as we use this in bootstrap.
7053 Roo.namespace('Roo.form');
7054  /**
7055  * @class Roo.form.Action
7056  * Internal Class used to handle form actions
7057  * @constructor
7058  * @param {Roo.form.BasicForm} el The form element or its id
7059  * @param {Object} config Configuration options
7060  */
7061
7062  
7063  
7064 // define the action interface
7065 Roo.form.Action = function(form, options){
7066     this.form = form;
7067     this.options = options || {};
7068 };
7069 /**
7070  * Client Validation Failed
7071  * @const 
7072  */
7073 Roo.form.Action.CLIENT_INVALID = 'client';
7074 /**
7075  * Server Validation Failed
7076  * @const 
7077  */
7078 Roo.form.Action.SERVER_INVALID = 'server';
7079  /**
7080  * Connect to Server Failed
7081  * @const 
7082  */
7083 Roo.form.Action.CONNECT_FAILURE = 'connect';
7084 /**
7085  * Reading Data from Server Failed
7086  * @const 
7087  */
7088 Roo.form.Action.LOAD_FAILURE = 'load';
7089
7090 Roo.form.Action.prototype = {
7091     type : 'default',
7092     failureType : undefined,
7093     response : undefined,
7094     result : undefined,
7095
7096     // interface method
7097     run : function(options){
7098
7099     },
7100
7101     // interface method
7102     success : function(response){
7103
7104     },
7105
7106     // interface method
7107     handleResponse : function(response){
7108
7109     },
7110
7111     // default connection failure
7112     failure : function(response){
7113         
7114         this.response = response;
7115         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7116         this.form.afterAction(this, false);
7117     },
7118
7119     processResponse : function(response){
7120         this.response = response;
7121         if(!response.responseText){
7122             return true;
7123         }
7124         this.result = this.handleResponse(response);
7125         return this.result;
7126     },
7127
7128     // utility functions used internally
7129     getUrl : function(appendParams){
7130         var url = this.options.url || this.form.url || this.form.el.dom.action;
7131         if(appendParams){
7132             var p = this.getParams();
7133             if(p){
7134                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7135             }
7136         }
7137         return url;
7138     },
7139
7140     getMethod : function(){
7141         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7142     },
7143
7144     getParams : function(){
7145         var bp = this.form.baseParams;
7146         var p = this.options.params;
7147         if(p){
7148             if(typeof p == "object"){
7149                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7150             }else if(typeof p == 'string' && bp){
7151                 p += '&' + Roo.urlEncode(bp);
7152             }
7153         }else if(bp){
7154             p = Roo.urlEncode(bp);
7155         }
7156         return p;
7157     },
7158
7159     createCallback : function(){
7160         return {
7161             success: this.success,
7162             failure: this.failure,
7163             scope: this,
7164             timeout: (this.form.timeout*1000),
7165             upload: this.form.fileUpload ? this.success : undefined
7166         };
7167     }
7168 };
7169
7170 Roo.form.Action.Submit = function(form, options){
7171     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7172 };
7173
7174 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7175     type : 'submit',
7176
7177     haveProgress : false,
7178     uploadComplete : false,
7179     
7180     // uploadProgress indicator.
7181     uploadProgress : function()
7182     {
7183         if (!this.form.progressUrl) {
7184             return;
7185         }
7186         
7187         if (!this.haveProgress) {
7188             Roo.MessageBox.progress("Uploading", "Uploading");
7189         }
7190         if (this.uploadComplete) {
7191            Roo.MessageBox.hide();
7192            return;
7193         }
7194         
7195         this.haveProgress = true;
7196    
7197         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7198         
7199         var c = new Roo.data.Connection();
7200         c.request({
7201             url : this.form.progressUrl,
7202             params: {
7203                 id : uid
7204             },
7205             method: 'GET',
7206             success : function(req){
7207                //console.log(data);
7208                 var rdata = false;
7209                 var edata;
7210                 try  {
7211                    rdata = Roo.decode(req.responseText)
7212                 } catch (e) {
7213                     Roo.log("Invalid data from server..");
7214                     Roo.log(edata);
7215                     return;
7216                 }
7217                 if (!rdata || !rdata.success) {
7218                     Roo.log(rdata);
7219                     Roo.MessageBox.alert(Roo.encode(rdata));
7220                     return;
7221                 }
7222                 var data = rdata.data;
7223                 
7224                 if (this.uploadComplete) {
7225                    Roo.MessageBox.hide();
7226                    return;
7227                 }
7228                    
7229                 if (data){
7230                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7231                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7232                     );
7233                 }
7234                 this.uploadProgress.defer(2000,this);
7235             },
7236        
7237             failure: function(data) {
7238                 Roo.log('progress url failed ');
7239                 Roo.log(data);
7240             },
7241             scope : this
7242         });
7243            
7244     },
7245     
7246     
7247     run : function()
7248     {
7249         // run get Values on the form, so it syncs any secondary forms.
7250         this.form.getValues();
7251         
7252         var o = this.options;
7253         var method = this.getMethod();
7254         var isPost = method == 'POST';
7255         if(o.clientValidation === false || this.form.isValid()){
7256             
7257             if (this.form.progressUrl) {
7258                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7259                     (new Date() * 1) + '' + Math.random());
7260                     
7261             } 
7262             
7263             
7264             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7265                 form:this.form.el.dom,
7266                 url:this.getUrl(!isPost),
7267                 method: method,
7268                 params:isPost ? this.getParams() : null,
7269                 isUpload: this.form.fileUpload
7270             }));
7271             
7272             this.uploadProgress();
7273
7274         }else if (o.clientValidation !== false){ // client validation failed
7275             this.failureType = Roo.form.Action.CLIENT_INVALID;
7276             this.form.afterAction(this, false);
7277         }
7278     },
7279
7280     success : function(response)
7281     {
7282         this.uploadComplete= true;
7283         if (this.haveProgress) {
7284             Roo.MessageBox.hide();
7285         }
7286         
7287         
7288         var result = this.processResponse(response);
7289         if(result === true || result.success){
7290             this.form.afterAction(this, true);
7291             return;
7292         }
7293         if(result.errors){
7294             this.form.markInvalid(result.errors);
7295             this.failureType = Roo.form.Action.SERVER_INVALID;
7296         }
7297         this.form.afterAction(this, false);
7298     },
7299     failure : function(response)
7300     {
7301         this.uploadComplete= true;
7302         if (this.haveProgress) {
7303             Roo.MessageBox.hide();
7304         }
7305         
7306         this.response = response;
7307         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7308         this.form.afterAction(this, false);
7309     },
7310     
7311     handleResponse : function(response){
7312         if(this.form.errorReader){
7313             var rs = this.form.errorReader.read(response);
7314             var errors = [];
7315             if(rs.records){
7316                 for(var i = 0, len = rs.records.length; i < len; i++) {
7317                     var r = rs.records[i];
7318                     errors[i] = r.data;
7319                 }
7320             }
7321             if(errors.length < 1){
7322                 errors = null;
7323             }
7324             return {
7325                 success : rs.success,
7326                 errors : errors
7327             };
7328         }
7329         var ret = false;
7330         try {
7331             ret = Roo.decode(response.responseText);
7332         } catch (e) {
7333             ret = {
7334                 success: false,
7335                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7336                 errors : []
7337             };
7338         }
7339         return ret;
7340         
7341     }
7342 });
7343
7344
7345 Roo.form.Action.Load = function(form, options){
7346     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7347     this.reader = this.form.reader;
7348 };
7349
7350 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7351     type : 'load',
7352
7353     run : function(){
7354         
7355         Roo.Ajax.request(Roo.apply(
7356                 this.createCallback(), {
7357                     method:this.getMethod(),
7358                     url:this.getUrl(false),
7359                     params:this.getParams()
7360         }));
7361     },
7362
7363     success : function(response){
7364         
7365         var result = this.processResponse(response);
7366         if(result === true || !result.success || !result.data){
7367             this.failureType = Roo.form.Action.LOAD_FAILURE;
7368             this.form.afterAction(this, false);
7369             return;
7370         }
7371         this.form.clearInvalid();
7372         this.form.setValues(result.data);
7373         this.form.afterAction(this, true);
7374     },
7375
7376     handleResponse : function(response){
7377         if(this.form.reader){
7378             var rs = this.form.reader.read(response);
7379             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7380             return {
7381                 success : rs.success,
7382                 data : data
7383             };
7384         }
7385         return Roo.decode(response.responseText);
7386     }
7387 });
7388
7389 Roo.form.Action.ACTION_TYPES = {
7390     'load' : Roo.form.Action.Load,
7391     'submit' : Roo.form.Action.Submit
7392 };/*
7393  * - LGPL
7394  *
7395  * form
7396  * 
7397  */
7398
7399 /**
7400  * @class Roo.bootstrap.Form
7401  * @extends Roo.bootstrap.Component
7402  * Bootstrap Form class
7403  * @cfg {String} method  GET | POST (default POST)
7404  * @cfg {String} labelAlign top | left (default top)
7405  * @cfg {String} align left  | right - for navbars
7406  * @cfg {Boolean} loadMask load mask when submit (default true)
7407
7408  * 
7409  * @constructor
7410  * Create a new Form
7411  * @param {Object} config The config object
7412  */
7413
7414
7415 Roo.bootstrap.Form = function(config){
7416     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7417     this.addEvents({
7418         /**
7419          * @event clientvalidation
7420          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7421          * @param {Form} this
7422          * @param {Boolean} valid true if the form has passed client-side validation
7423          */
7424         clientvalidation: true,
7425         /**
7426          * @event beforeaction
7427          * Fires before any action is performed. Return false to cancel the action.
7428          * @param {Form} this
7429          * @param {Action} action The action to be performed
7430          */
7431         beforeaction: true,
7432         /**
7433          * @event actionfailed
7434          * Fires when an action fails.
7435          * @param {Form} this
7436          * @param {Action} action The action that failed
7437          */
7438         actionfailed : true,
7439         /**
7440          * @event actioncomplete
7441          * Fires when an action is completed.
7442          * @param {Form} this
7443          * @param {Action} action The action that completed
7444          */
7445         actioncomplete : true
7446     });
7447     
7448 };
7449
7450 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7451       
7452      /**
7453      * @cfg {String} method
7454      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7455      */
7456     method : 'POST',
7457     /**
7458      * @cfg {String} url
7459      * The URL to use for form actions if one isn't supplied in the action options.
7460      */
7461     /**
7462      * @cfg {Boolean} fileUpload
7463      * Set to true if this form is a file upload.
7464      */
7465      
7466     /**
7467      * @cfg {Object} baseParams
7468      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7469      */
7470       
7471     /**
7472      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7473      */
7474     timeout: 30,
7475     /**
7476      * @cfg {Sting} align (left|right) for navbar forms
7477      */
7478     align : 'left',
7479
7480     // private
7481     activeAction : null,
7482  
7483     /**
7484      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7485      * element by passing it or its id or mask the form itself by passing in true.
7486      * @type Mixed
7487      */
7488     waitMsgTarget : false,
7489     
7490     loadMask : true,
7491     
7492     getAutoCreate : function(){
7493         
7494         var cfg = {
7495             tag: 'form',
7496             method : this.method || 'POST',
7497             id : this.id || Roo.id(),
7498             cls : ''
7499         };
7500         if (this.parent().xtype.match(/^Nav/)) {
7501             cfg.cls = 'navbar-form navbar-' + this.align;
7502             
7503         }
7504         
7505         if (this.labelAlign == 'left' ) {
7506             cfg.cls += ' form-horizontal';
7507         }
7508         
7509         
7510         return cfg;
7511     },
7512     initEvents : function()
7513     {
7514         this.el.on('submit', this.onSubmit, this);
7515         // this was added as random key presses on the form where triggering form submit.
7516         this.el.on('keypress', function(e) {
7517             if (e.getCharCode() != 13) {
7518                 return true;
7519             }
7520             // we might need to allow it for textareas.. and some other items.
7521             // check e.getTarget().
7522             
7523             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7524                 return true;
7525             }
7526         
7527             Roo.log("keypress blocked");
7528             
7529             e.preventDefault();
7530             return false;
7531         });
7532         
7533     },
7534     // private
7535     onSubmit : function(e){
7536         e.stopEvent();
7537     },
7538     
7539      /**
7540      * Returns true if client-side validation on the form is successful.
7541      * @return Boolean
7542      */
7543     isValid : function(){
7544         var items = this.getItems();
7545         var valid = true;
7546         items.each(function(f){
7547            if(!f.validate()){
7548                valid = false;
7549                
7550            }
7551         });
7552         return valid;
7553     },
7554     /**
7555      * Returns true if any fields in this form have changed since their original load.
7556      * @return Boolean
7557      */
7558     isDirty : function(){
7559         var dirty = false;
7560         var items = this.getItems();
7561         items.each(function(f){
7562            if(f.isDirty()){
7563                dirty = true;
7564                return false;
7565            }
7566            return true;
7567         });
7568         return dirty;
7569     },
7570      /**
7571      * Performs a predefined action (submit or load) or custom actions you define on this form.
7572      * @param {String} actionName The name of the action type
7573      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7574      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7575      * accept other config options):
7576      * <pre>
7577 Property          Type             Description
7578 ----------------  ---------------  ----------------------------------------------------------------------------------
7579 url               String           The url for the action (defaults to the form's url)
7580 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7581 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7582 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7583                                    validate the form on the client (defaults to false)
7584      * </pre>
7585      * @return {BasicForm} this
7586      */
7587     doAction : function(action, options){
7588         if(typeof action == 'string'){
7589             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7590         }
7591         if(this.fireEvent('beforeaction', this, action) !== false){
7592             this.beforeAction(action);
7593             action.run.defer(100, action);
7594         }
7595         return this;
7596     },
7597     
7598     // private
7599     beforeAction : function(action){
7600         var o = action.options;
7601         
7602         if(this.loadMask){
7603             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7604         }
7605         // not really supported yet.. ??
7606         
7607         //if(this.waitMsgTarget === true){
7608         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7609         //}else if(this.waitMsgTarget){
7610         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7611         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7612         //}else {
7613         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7614        // }
7615          
7616     },
7617
7618     // private
7619     afterAction : function(action, success){
7620         this.activeAction = null;
7621         var o = action.options;
7622         
7623         //if(this.waitMsgTarget === true){
7624             this.el.unmask();
7625         //}else if(this.waitMsgTarget){
7626         //    this.waitMsgTarget.unmask();
7627         //}else{
7628         //    Roo.MessageBox.updateProgress(1);
7629         //    Roo.MessageBox.hide();
7630        // }
7631         // 
7632         if(success){
7633             if(o.reset){
7634                 this.reset();
7635             }
7636             Roo.callback(o.success, o.scope, [this, action]);
7637             this.fireEvent('actioncomplete', this, action);
7638             
7639         }else{
7640             
7641             // failure condition..
7642             // we have a scenario where updates need confirming.
7643             // eg. if a locking scenario exists..
7644             // we look for { errors : { needs_confirm : true }} in the response.
7645             if (
7646                 (typeof(action.result) != 'undefined')  &&
7647                 (typeof(action.result.errors) != 'undefined')  &&
7648                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7649            ){
7650                 var _t = this;
7651                 Roo.log("not supported yet");
7652                  /*
7653                 
7654                 Roo.MessageBox.confirm(
7655                     "Change requires confirmation",
7656                     action.result.errorMsg,
7657                     function(r) {
7658                         if (r != 'yes') {
7659                             return;
7660                         }
7661                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7662                     }
7663                     
7664                 );
7665                 */
7666                 
7667                 
7668                 return;
7669             }
7670             
7671             Roo.callback(o.failure, o.scope, [this, action]);
7672             // show an error message if no failed handler is set..
7673             if (!this.hasListener('actionfailed')) {
7674                 Roo.log("need to add dialog support");
7675                 /*
7676                 Roo.MessageBox.alert("Error",
7677                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7678                         action.result.errorMsg :
7679                         "Saving Failed, please check your entries or try again"
7680                 );
7681                 */
7682             }
7683             
7684             this.fireEvent('actionfailed', this, action);
7685         }
7686         
7687     },
7688     /**
7689      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7690      * @param {String} id The value to search for
7691      * @return Field
7692      */
7693     findField : function(id){
7694         var items = this.getItems();
7695         var field = items.get(id);
7696         if(!field){
7697              items.each(function(f){
7698                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7699                     field = f;
7700                     return false;
7701                 }
7702                 return true;
7703             });
7704         }
7705         return field || null;
7706     },
7707      /**
7708      * Mark fields in this form invalid in bulk.
7709      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7710      * @return {BasicForm} this
7711      */
7712     markInvalid : function(errors){
7713         if(errors instanceof Array){
7714             for(var i = 0, len = errors.length; i < len; i++){
7715                 var fieldError = errors[i];
7716                 var f = this.findField(fieldError.id);
7717                 if(f){
7718                     f.markInvalid(fieldError.msg);
7719                 }
7720             }
7721         }else{
7722             var field, id;
7723             for(id in errors){
7724                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7725                     field.markInvalid(errors[id]);
7726                 }
7727             }
7728         }
7729         //Roo.each(this.childForms || [], function (f) {
7730         //    f.markInvalid(errors);
7731         //});
7732         
7733         return this;
7734     },
7735
7736     /**
7737      * Set values for fields in this form in bulk.
7738      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7739      * @return {BasicForm} this
7740      */
7741     setValues : function(values){
7742         if(values instanceof Array){ // array of objects
7743             for(var i = 0, len = values.length; i < len; i++){
7744                 var v = values[i];
7745                 var f = this.findField(v.id);
7746                 if(f){
7747                     f.setValue(v.value);
7748                     if(this.trackResetOnLoad){
7749                         f.originalValue = f.getValue();
7750                     }
7751                 }
7752             }
7753         }else{ // object hash
7754             var field, id;
7755             for(id in values){
7756                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7757                     
7758                     if (field.setFromData && 
7759                         field.valueField && 
7760                         field.displayField &&
7761                         // combos' with local stores can 
7762                         // be queried via setValue()
7763                         // to set their value..
7764                         (field.store && !field.store.isLocal)
7765                         ) {
7766                         // it's a combo
7767                         var sd = { };
7768                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7769                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7770                         field.setFromData(sd);
7771                         
7772                     } else {
7773                         field.setValue(values[id]);
7774                     }
7775                     
7776                     
7777                     if(this.trackResetOnLoad){
7778                         field.originalValue = field.getValue();
7779                     }
7780                 }
7781             }
7782         }
7783          
7784         //Roo.each(this.childForms || [], function (f) {
7785         //    f.setValues(values);
7786         //});
7787                 
7788         return this;
7789     },
7790
7791     /**
7792      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7793      * they are returned as an array.
7794      * @param {Boolean} asString
7795      * @return {Object}
7796      */
7797     getValues : function(asString){
7798         //if (this.childForms) {
7799             // copy values from the child forms
7800         //    Roo.each(this.childForms, function (f) {
7801         //        this.setValues(f.getValues());
7802         //    }, this);
7803         //}
7804         
7805         
7806         
7807         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7808         if(asString === true){
7809             return fs;
7810         }
7811         return Roo.urlDecode(fs);
7812     },
7813     
7814     /**
7815      * Returns the fields in this form as an object with key/value pairs. 
7816      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7817      * @return {Object}
7818      */
7819     getFieldValues : function(with_hidden)
7820     {
7821         var items = this.getItems();
7822         var ret = {};
7823         items.each(function(f){
7824             if (!f.getName()) {
7825                 return;
7826             }
7827             var v = f.getValue();
7828             if (f.inputType =='radio') {
7829                 if (typeof(ret[f.getName()]) == 'undefined') {
7830                     ret[f.getName()] = ''; // empty..
7831                 }
7832                 
7833                 if (!f.el.dom.checked) {
7834                     return;
7835                     
7836                 }
7837                 v = f.el.dom.value;
7838                 
7839             }
7840             
7841             // not sure if this supported any more..
7842             if ((typeof(v) == 'object') && f.getRawValue) {
7843                 v = f.getRawValue() ; // dates..
7844             }
7845             // combo boxes where name != hiddenName...
7846             if (f.name != f.getName()) {
7847                 ret[f.name] = f.getRawValue();
7848             }
7849             ret[f.getName()] = v;
7850         });
7851         
7852         return ret;
7853     },
7854
7855     /**
7856      * Clears all invalid messages in this form.
7857      * @return {BasicForm} this
7858      */
7859     clearInvalid : function(){
7860         var items = this.getItems();
7861         
7862         items.each(function(f){
7863            f.clearInvalid();
7864         });
7865         
7866         
7867         
7868         return this;
7869     },
7870
7871     /**
7872      * Resets this form.
7873      * @return {BasicForm} this
7874      */
7875     reset : function(){
7876         var items = this.getItems();
7877         items.each(function(f){
7878             f.reset();
7879         });
7880         
7881         Roo.each(this.childForms || [], function (f) {
7882             f.reset();
7883         });
7884        
7885         
7886         return this;
7887     },
7888     getItems : function()
7889     {
7890         var r=new Roo.util.MixedCollection(false, function(o){
7891             return o.id || (o.id = Roo.id());
7892         });
7893         var iter = function(el) {
7894             if (el.inputEl) {
7895                 r.add(el);
7896             }
7897             if (!el.items) {
7898                 return;
7899             }
7900             Roo.each(el.items,function(e) {
7901                 iter(e);
7902             });
7903             
7904             
7905         };
7906         
7907         iter(this);
7908         return r;
7909         
7910         
7911         
7912         
7913     }
7914     
7915 });
7916
7917  
7918 /*
7919  * Based on:
7920  * Ext JS Library 1.1.1
7921  * Copyright(c) 2006-2007, Ext JS, LLC.
7922  *
7923  * Originally Released Under LGPL - original licence link has changed is not relivant.
7924  *
7925  * Fork - LGPL
7926  * <script type="text/javascript">
7927  */
7928 /**
7929  * @class Roo.form.VTypes
7930  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7931  * @singleton
7932  */
7933 Roo.form.VTypes = function(){
7934     // closure these in so they are only created once.
7935     var alpha = /^[a-zA-Z_]+$/;
7936     var alphanum = /^[a-zA-Z0-9_]+$/;
7937     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7938     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7939
7940     // All these messages and functions are configurable
7941     return {
7942         /**
7943          * The function used to validate email addresses
7944          * @param {String} value The email address
7945          */
7946         'email' : function(v){
7947             return email.test(v);
7948         },
7949         /**
7950          * The error text to display when the email validation function returns false
7951          * @type String
7952          */
7953         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7954         /**
7955          * The keystroke filter mask to be applied on email input
7956          * @type RegExp
7957          */
7958         'emailMask' : /[a-z0-9_\.\-@]/i,
7959
7960         /**
7961          * The function used to validate URLs
7962          * @param {String} value The URL
7963          */
7964         'url' : function(v){
7965             return url.test(v);
7966         },
7967         /**
7968          * The error text to display when the url validation function returns false
7969          * @type String
7970          */
7971         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7972         
7973         /**
7974          * The function used to validate alpha values
7975          * @param {String} value The value
7976          */
7977         'alpha' : function(v){
7978             return alpha.test(v);
7979         },
7980         /**
7981          * The error text to display when the alpha validation function returns false
7982          * @type String
7983          */
7984         'alphaText' : 'This field should only contain letters and _',
7985         /**
7986          * The keystroke filter mask to be applied on alpha input
7987          * @type RegExp
7988          */
7989         'alphaMask' : /[a-z_]/i,
7990
7991         /**
7992          * The function used to validate alphanumeric values
7993          * @param {String} value The value
7994          */
7995         'alphanum' : function(v){
7996             return alphanum.test(v);
7997         },
7998         /**
7999          * The error text to display when the alphanumeric validation function returns false
8000          * @type String
8001          */
8002         'alphanumText' : 'This field should only contain letters, numbers and _',
8003         /**
8004          * The keystroke filter mask to be applied on alphanumeric input
8005          * @type RegExp
8006          */
8007         'alphanumMask' : /[a-z0-9_]/i
8008     };
8009 }();/*
8010  * - LGPL
8011  *
8012  * Input
8013  * 
8014  */
8015
8016 /**
8017  * @class Roo.bootstrap.Input
8018  * @extends Roo.bootstrap.Component
8019  * Bootstrap Input class
8020  * @cfg {Boolean} disabled is it disabled
8021  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8022  * @cfg {String} name name of the input
8023  * @cfg {string} fieldLabel - the label associated
8024  * @cfg {string} placeholder - placeholder to put in text.
8025  * @cfg {string}  before - input group add on before
8026  * @cfg {string} after - input group add on after
8027  * @cfg {string} size - (lg|sm) or leave empty..
8028  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8029  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8030  * @cfg {Number} md colspan out of 12 for computer-sized screens
8031  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8032  * @cfg {string} value default value of the input
8033  * @cfg {Number} labelWidth set the width of label (0-12)
8034  * @cfg {String} labelAlign (top|left)
8035  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8036  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8037  * @cfg {String} indicatorpos (left|right) default left
8038
8039  * @cfg {String} align (left|center|right) Default left
8040  * @cfg {Boolean} forceFeedback (true|false) Default false
8041  * 
8042  * 
8043  * 
8044  * 
8045  * @constructor
8046  * Create a new Input
8047  * @param {Object} config The config object
8048  */
8049
8050 Roo.bootstrap.Input = function(config){
8051     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8052    
8053         this.addEvents({
8054             /**
8055              * @event focus
8056              * Fires when this field receives input focus.
8057              * @param {Roo.form.Field} this
8058              */
8059             focus : true,
8060             /**
8061              * @event blur
8062              * Fires when this field loses input focus.
8063              * @param {Roo.form.Field} this
8064              */
8065             blur : true,
8066             /**
8067              * @event specialkey
8068              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8069              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8070              * @param {Roo.form.Field} this
8071              * @param {Roo.EventObject} e The event object
8072              */
8073             specialkey : true,
8074             /**
8075              * @event change
8076              * Fires just before the field blurs if the field value has changed.
8077              * @param {Roo.form.Field} this
8078              * @param {Mixed} newValue The new value
8079              * @param {Mixed} oldValue The original value
8080              */
8081             change : true,
8082             /**
8083              * @event invalid
8084              * Fires after the field has been marked as invalid.
8085              * @param {Roo.form.Field} this
8086              * @param {String} msg The validation message
8087              */
8088             invalid : true,
8089             /**
8090              * @event valid
8091              * Fires after the field has been validated with no errors.
8092              * @param {Roo.form.Field} this
8093              */
8094             valid : true,
8095              /**
8096              * @event keyup
8097              * Fires after the key up
8098              * @param {Roo.form.Field} this
8099              * @param {Roo.EventObject}  e The event Object
8100              */
8101             keyup : true
8102         });
8103 };
8104
8105 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8106      /**
8107      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8108       automatic validation (defaults to "keyup").
8109      */
8110     validationEvent : "keyup",
8111      /**
8112      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8113      */
8114     validateOnBlur : true,
8115     /**
8116      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8117      */
8118     validationDelay : 250,
8119      /**
8120      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8121      */
8122     focusClass : "x-form-focus",  // not needed???
8123     
8124        
8125     /**
8126      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8127      */
8128     invalidClass : "has-warning",
8129     
8130     /**
8131      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8132      */
8133     validClass : "has-success",
8134     
8135     /**
8136      * @cfg {Boolean} hasFeedback (true|false) default true
8137      */
8138     hasFeedback : true,
8139     
8140     /**
8141      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8142      */
8143     invalidFeedbackClass : "glyphicon-warning-sign",
8144     
8145     /**
8146      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8147      */
8148     validFeedbackClass : "glyphicon-ok",
8149     
8150     /**
8151      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8152      */
8153     selectOnFocus : false,
8154     
8155      /**
8156      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8157      */
8158     maskRe : null,
8159        /**
8160      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8161      */
8162     vtype : null,
8163     
8164       /**
8165      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8166      */
8167     disableKeyFilter : false,
8168     
8169        /**
8170      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8171      */
8172     disabled : false,
8173      /**
8174      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8175      */
8176     allowBlank : true,
8177     /**
8178      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8179      */
8180     blankText : "This field is required",
8181     
8182      /**
8183      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8184      */
8185     minLength : 0,
8186     /**
8187      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8188      */
8189     maxLength : Number.MAX_VALUE,
8190     /**
8191      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8192      */
8193     minLengthText : "The minimum length for this field is {0}",
8194     /**
8195      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8196      */
8197     maxLengthText : "The maximum length for this field is {0}",
8198   
8199     
8200     /**
8201      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8202      * If available, this function will be called only after the basic validators all return true, and will be passed the
8203      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8204      */
8205     validator : null,
8206     /**
8207      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8208      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8209      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8210      */
8211     regex : null,
8212     /**
8213      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8214      */
8215     regexText : "",
8216     
8217     autocomplete: false,
8218     
8219     
8220     fieldLabel : '',
8221     inputType : 'text',
8222     
8223     name : false,
8224     placeholder: false,
8225     before : false,
8226     after : false,
8227     size : false,
8228     hasFocus : false,
8229     preventMark: false,
8230     isFormField : true,
8231     value : '',
8232     labelWidth : 2,
8233     labelAlign : false,
8234     readOnly : false,
8235     align : false,
8236     formatedValue : false,
8237     forceFeedback : false,
8238     
8239     indicatorpos : 'left',
8240     
8241     parentLabelAlign : function()
8242     {
8243         var parent = this;
8244         while (parent.parent()) {
8245             parent = parent.parent();
8246             if (typeof(parent.labelAlign) !='undefined') {
8247                 return parent.labelAlign;
8248             }
8249         }
8250         return 'left';
8251         
8252     },
8253     
8254     getAutoCreate : function()
8255     {
8256         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8257         
8258         var id = Roo.id();
8259         
8260         var cfg = {};
8261         
8262         if(this.inputType != 'hidden'){
8263             cfg.cls = 'form-group' //input-group
8264         }
8265         
8266         var input =  {
8267             tag: 'input',
8268             id : id,
8269             type : this.inputType,
8270             value : this.value,
8271             cls : 'form-control',
8272             placeholder : this.placeholder || '',
8273             autocomplete : this.autocomplete || 'new-password'
8274         };
8275         
8276         if(this.align){
8277             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8278         }
8279         
8280         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8281             input.maxLength = this.maxLength;
8282         }
8283         
8284         if (this.disabled) {
8285             input.disabled=true;
8286         }
8287         
8288         if (this.readOnly) {
8289             input.readonly=true;
8290         }
8291         
8292         if (this.name) {
8293             input.name = this.name;
8294         }
8295         
8296         if (this.size) {
8297             input.cls += ' input-' + this.size;
8298         }
8299         
8300         var settings=this;
8301         ['xs','sm','md','lg'].map(function(size){
8302             if (settings[size]) {
8303                 cfg.cls += ' col-' + size + '-' + settings[size];
8304             }
8305         });
8306         
8307         var inputblock = input;
8308         
8309         var feedback = {
8310             tag: 'span',
8311             cls: 'glyphicon form-control-feedback'
8312         };
8313             
8314         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8315             
8316             inputblock = {
8317                 cls : 'has-feedback',
8318                 cn :  [
8319                     input,
8320                     feedback
8321                 ] 
8322             };  
8323         }
8324         
8325         if (this.before || this.after) {
8326             
8327             inputblock = {
8328                 cls : 'input-group',
8329                 cn :  [] 
8330             };
8331             
8332             if (this.before && typeof(this.before) == 'string') {
8333                 
8334                 inputblock.cn.push({
8335                     tag :'span',
8336                     cls : 'roo-input-before input-group-addon',
8337                     html : this.before
8338                 });
8339             }
8340             if (this.before && typeof(this.before) == 'object') {
8341                 this.before = Roo.factory(this.before);
8342                 
8343                 inputblock.cn.push({
8344                     tag :'span',
8345                     cls : 'roo-input-before input-group-' +
8346                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8347                 });
8348             }
8349             
8350             inputblock.cn.push(input);
8351             
8352             if (this.after && typeof(this.after) == 'string') {
8353                 inputblock.cn.push({
8354                     tag :'span',
8355                     cls : 'roo-input-after input-group-addon',
8356                     html : this.after
8357                 });
8358             }
8359             if (this.after && typeof(this.after) == 'object') {
8360                 this.after = Roo.factory(this.after);
8361                 
8362                 inputblock.cn.push({
8363                     tag :'span',
8364                     cls : 'roo-input-after input-group-' +
8365                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8366                 });
8367             }
8368             
8369             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8370                 inputblock.cls += ' has-feedback';
8371                 inputblock.cn.push(feedback);
8372             }
8373         };
8374         
8375         if (align ==='left' && this.fieldLabel.length) {
8376             
8377             cfg.cn = [
8378                 {
8379                     tag : 'i',
8380                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8381                     tooltip : 'This field is required'
8382                 },
8383                 {
8384                     tag: 'label',
8385                     'for' :  id,
8386                     cls : 'control-label col-sm-' + this.labelWidth,
8387                     html : this.fieldLabel
8388
8389                 },
8390                 {
8391                     cls : "col-sm-" + (12 - this.labelWidth), 
8392                     cn: [
8393                         inputblock
8394                     ]
8395                 }
8396
8397             ];
8398             
8399             if(this.indicatorpos == 'right'){
8400                 cfg.cn = [
8401                     {
8402                         tag: 'label',
8403                         'for' :  id,
8404                         cls : 'control-label col-sm-' + this.labelWidth,
8405                         html : this.fieldLabel
8406
8407                     },
8408                     {
8409                         tag : 'i',
8410                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8411                         tooltip : 'This field is required'
8412                     },
8413                     {
8414                         cls : "col-sm-" + (12 - this.labelWidth), 
8415                         cn: [
8416                             inputblock
8417                         ]
8418                     }
8419
8420                 ];
8421             }
8422             
8423         } else if ( this.fieldLabel.length) {
8424                 
8425             cfg.cn = [
8426                 {
8427                     tag : 'i',
8428                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8429                     tooltip : 'This field is required'
8430                 },
8431                 {
8432                     tag: 'label',
8433                    //cls : 'input-group-addon',
8434                     html : this.fieldLabel
8435
8436                 },
8437
8438                inputblock
8439
8440            ];
8441            
8442            if(this.indicatorpos == 'right'){
8443                 
8444                 cfg.cn = [
8445                     {
8446                         tag: 'label',
8447                        //cls : 'input-group-addon',
8448                         html : this.fieldLabel
8449
8450                     },
8451                     {
8452                         tag : 'i',
8453                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8454                         tooltip : 'This field is required'
8455                     },
8456
8457                    inputblock
8458
8459                ];
8460
8461             }
8462
8463         } else {
8464             
8465             cfg.cn = [
8466
8467                     inputblock
8468
8469             ];
8470                 
8471                 
8472         };
8473         
8474         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8475            cfg.cls += ' navbar-form';
8476         }
8477         
8478         if (this.parentType === 'NavGroup') {
8479            cfg.cls += ' navbar-form';
8480            cfg.tag = 'li';
8481         }
8482         
8483         return cfg;
8484         
8485     },
8486     /**
8487      * return the real input element.
8488      */
8489     inputEl: function ()
8490     {
8491         return this.el.select('input.form-control',true).first();
8492     },
8493     
8494     tooltipEl : function()
8495     {
8496         return this.inputEl();
8497     },
8498     
8499     indicatorEl : function()
8500     {
8501         var indicator = this.el.select('i.roo-required-indicator',true).first();
8502         
8503         if(!indicator){
8504             return false;
8505         }
8506         
8507         return indicator;
8508         
8509     },
8510     
8511     setDisabled : function(v)
8512     {
8513         var i  = this.inputEl().dom;
8514         if (!v) {
8515             i.removeAttribute('disabled');
8516             return;
8517             
8518         }
8519         i.setAttribute('disabled','true');
8520     },
8521     initEvents : function()
8522     {
8523           
8524         this.inputEl().on("keydown" , this.fireKey,  this);
8525         this.inputEl().on("focus", this.onFocus,  this);
8526         this.inputEl().on("blur", this.onBlur,  this);
8527         
8528         this.inputEl().relayEvent('keyup', this);
8529         
8530         this.indicator = this.indicatorEl();
8531         
8532         if(this.indicator){
8533             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8534             this.indicator.hide();
8535         }
8536  
8537         // reference to original value for reset
8538         this.originalValue = this.getValue();
8539         //Roo.form.TextField.superclass.initEvents.call(this);
8540         if(this.validationEvent == 'keyup'){
8541             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8542             this.inputEl().on('keyup', this.filterValidation, this);
8543         }
8544         else if(this.validationEvent !== false){
8545             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8546         }
8547         
8548         if(this.selectOnFocus){
8549             this.on("focus", this.preFocus, this);
8550             
8551         }
8552         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8553             this.inputEl().on("keypress", this.filterKeys, this);
8554         } else {
8555             this.inputEl().relayEvent('keypress', this);
8556         }
8557        /* if(this.grow){
8558             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8559             this.el.on("click", this.autoSize,  this);
8560         }
8561         */
8562         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8563             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8564         }
8565         
8566         if (typeof(this.before) == 'object') {
8567             this.before.render(this.el.select('.roo-input-before',true).first());
8568         }
8569         if (typeof(this.after) == 'object') {
8570             this.after.render(this.el.select('.roo-input-after',true).first());
8571         }
8572         
8573         
8574     },
8575     filterValidation : function(e){
8576         if(!e.isNavKeyPress()){
8577             this.validationTask.delay(this.validationDelay);
8578         }
8579     },
8580      /**
8581      * Validates the field value
8582      * @return {Boolean} True if the value is valid, else false
8583      */
8584     validate : function(){
8585         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8586         if(this.disabled || this.validateValue(this.getRawValue())){
8587             this.markValid();
8588             return true;
8589         }
8590         
8591         this.markInvalid();
8592         return false;
8593     },
8594     
8595     
8596     /**
8597      * Validates a value according to the field's validation rules and marks the field as invalid
8598      * if the validation fails
8599      * @param {Mixed} value The value to validate
8600      * @return {Boolean} True if the value is valid, else false
8601      */
8602     validateValue : function(value){
8603         if(value.length < 1)  { // if it's blank
8604             if(this.allowBlank){
8605                 return true;
8606             }
8607             return false;
8608         }
8609         
8610         if(value.length < this.minLength){
8611             return false;
8612         }
8613         if(value.length > this.maxLength){
8614             return false;
8615         }
8616         if(this.vtype){
8617             var vt = Roo.form.VTypes;
8618             if(!vt[this.vtype](value, this)){
8619                 return false;
8620             }
8621         }
8622         if(typeof this.validator == "function"){
8623             var msg = this.validator(value);
8624             if(msg !== true){
8625                 return false;
8626             }
8627         }
8628         
8629         if(this.regex && !this.regex.test(value)){
8630             return false;
8631         }
8632         
8633         return true;
8634     },
8635
8636     
8637     
8638      // private
8639     fireKey : function(e){
8640         //Roo.log('field ' + e.getKey());
8641         if(e.isNavKeyPress()){
8642             this.fireEvent("specialkey", this, e);
8643         }
8644     },
8645     focus : function (selectText){
8646         if(this.rendered){
8647             this.inputEl().focus();
8648             if(selectText === true){
8649                 this.inputEl().dom.select();
8650             }
8651         }
8652         return this;
8653     } ,
8654     
8655     onFocus : function(){
8656         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8657            // this.el.addClass(this.focusClass);
8658         }
8659         if(!this.hasFocus){
8660             this.hasFocus = true;
8661             this.startValue = this.getValue();
8662             this.fireEvent("focus", this);
8663         }
8664     },
8665     
8666     beforeBlur : Roo.emptyFn,
8667
8668     
8669     // private
8670     onBlur : function(){
8671         this.beforeBlur();
8672         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8673             //this.el.removeClass(this.focusClass);
8674         }
8675         this.hasFocus = false;
8676         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8677             this.validate();
8678         }
8679         var v = this.getValue();
8680         if(String(v) !== String(this.startValue)){
8681             this.fireEvent('change', this, v, this.startValue);
8682         }
8683         this.fireEvent("blur", this);
8684     },
8685     
8686     /**
8687      * Resets the current field value to the originally loaded value and clears any validation messages
8688      */
8689     reset : function(){
8690         this.setValue(this.originalValue);
8691         this.validate();
8692     },
8693      /**
8694      * Returns the name of the field
8695      * @return {Mixed} name The name field
8696      */
8697     getName: function(){
8698         return this.name;
8699     },
8700      /**
8701      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8702      * @return {Mixed} value The field value
8703      */
8704     getValue : function(){
8705         
8706         var v = this.inputEl().getValue();
8707         
8708         return v;
8709     },
8710     /**
8711      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8712      * @return {Mixed} value The field value
8713      */
8714     getRawValue : function(){
8715         var v = this.inputEl().getValue();
8716         
8717         return v;
8718     },
8719     
8720     /**
8721      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8722      * @param {Mixed} value The value to set
8723      */
8724     setRawValue : function(v){
8725         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8726     },
8727     
8728     selectText : function(start, end){
8729         var v = this.getRawValue();
8730         if(v.length > 0){
8731             start = start === undefined ? 0 : start;
8732             end = end === undefined ? v.length : end;
8733             var d = this.inputEl().dom;
8734             if(d.setSelectionRange){
8735                 d.setSelectionRange(start, end);
8736             }else if(d.createTextRange){
8737                 var range = d.createTextRange();
8738                 range.moveStart("character", start);
8739                 range.moveEnd("character", v.length-end);
8740                 range.select();
8741             }
8742         }
8743     },
8744     
8745     /**
8746      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8747      * @param {Mixed} value The value to set
8748      */
8749     setValue : function(v){
8750         this.value = v;
8751         if(this.rendered){
8752             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8753             this.validate();
8754         }
8755     },
8756     
8757     /*
8758     processValue : function(value){
8759         if(this.stripCharsRe){
8760             var newValue = value.replace(this.stripCharsRe, '');
8761             if(newValue !== value){
8762                 this.setRawValue(newValue);
8763                 return newValue;
8764             }
8765         }
8766         return value;
8767     },
8768   */
8769     preFocus : function(){
8770         
8771         if(this.selectOnFocus){
8772             this.inputEl().dom.select();
8773         }
8774     },
8775     filterKeys : function(e){
8776         var k = e.getKey();
8777         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8778             return;
8779         }
8780         var c = e.getCharCode(), cc = String.fromCharCode(c);
8781         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8782             return;
8783         }
8784         if(!this.maskRe.test(cc)){
8785             e.stopEvent();
8786         }
8787     },
8788      /**
8789      * Clear any invalid styles/messages for this field
8790      */
8791     clearInvalid : function(){
8792         
8793         if(!this.el || this.preventMark){ // not rendered
8794             return;
8795         }
8796         
8797         if(this.indicator){
8798             this.indicator.hide();
8799         }
8800         
8801         this.el.removeClass(this.invalidClass);
8802         
8803         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8804             
8805             var feedback = this.el.select('.form-control-feedback', true).first();
8806             
8807             if(feedback){
8808                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8809             }
8810             
8811         }
8812         
8813         this.fireEvent('valid', this);
8814     },
8815     
8816      /**
8817      * Mark this field as valid
8818      */
8819     markValid : function()
8820     {
8821         if(!this.el  || this.preventMark){ // not rendered
8822             return;
8823         }
8824         
8825         this.el.removeClass([this.invalidClass, this.validClass]);
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         }
8832
8833         if(this.disabled || this.allowBlank){
8834             return;
8835         }
8836         
8837         if(this.indicator){
8838             this.indicator.hide();
8839         }
8840         
8841         this.el.addClass(this.validClass);
8842         
8843         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8844             
8845             var feedback = this.el.select('.form-control-feedback', true).first();
8846             
8847             if(feedback){
8848                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8849                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8850             }
8851             
8852         }
8853         
8854         this.fireEvent('valid', this);
8855     },
8856     
8857      /**
8858      * Mark this field as invalid
8859      * @param {String} msg The validation message
8860      */
8861     markInvalid : function(msg)
8862     {
8863         if(!this.el  || this.preventMark){ // not rendered
8864             return;
8865         }
8866         
8867         this.el.removeClass([this.invalidClass, this.validClass]);
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
8875         if(this.disabled || this.allowBlank){
8876             return;
8877         }
8878         
8879         if(this.indicator){
8880             this.indicator.show();
8881         }
8882         
8883         this.el.addClass(this.invalidClass);
8884         
8885         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8886             
8887             var feedback = this.el.select('.form-control-feedback', true).first();
8888             
8889             if(feedback){
8890                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8891                 
8892                 if(this.getValue().length || this.forceFeedback){
8893                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8894                 }
8895                 
8896             }
8897             
8898         }
8899         
8900         this.fireEvent('invalid', this, msg);
8901     },
8902     // private
8903     SafariOnKeyDown : function(event)
8904     {
8905         // this is a workaround for a password hang bug on chrome/ webkit.
8906         
8907         var isSelectAll = false;
8908         
8909         if(this.inputEl().dom.selectionEnd > 0){
8910             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8911         }
8912         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8913             event.preventDefault();
8914             this.setValue('');
8915             return;
8916         }
8917         
8918         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8919             
8920             event.preventDefault();
8921             // this is very hacky as keydown always get's upper case.
8922             //
8923             var cc = String.fromCharCode(event.getCharCode());
8924             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8925             
8926         }
8927     },
8928     adjustWidth : function(tag, w){
8929         tag = tag.toLowerCase();
8930         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8931             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8932                 if(tag == 'input'){
8933                     return w + 2;
8934                 }
8935                 if(tag == 'textarea'){
8936                     return w-2;
8937                 }
8938             }else if(Roo.isOpera){
8939                 if(tag == 'input'){
8940                     return w + 2;
8941                 }
8942                 if(tag == 'textarea'){
8943                     return w-2;
8944                 }
8945             }
8946         }
8947         return w;
8948     }
8949     
8950 });
8951
8952  
8953 /*
8954  * - LGPL
8955  *
8956  * Input
8957  * 
8958  */
8959
8960 /**
8961  * @class Roo.bootstrap.TextArea
8962  * @extends Roo.bootstrap.Input
8963  * Bootstrap TextArea class
8964  * @cfg {Number} cols Specifies the visible width of a text area
8965  * @cfg {Number} rows Specifies the visible number of lines in a text area
8966  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8967  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8968  * @cfg {string} html text
8969  * 
8970  * @constructor
8971  * Create a new TextArea
8972  * @param {Object} config The config object
8973  */
8974
8975 Roo.bootstrap.TextArea = function(config){
8976     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8977    
8978 };
8979
8980 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8981      
8982     cols : false,
8983     rows : 5,
8984     readOnly : false,
8985     warp : 'soft',
8986     resize : false,
8987     value: false,
8988     html: false,
8989     
8990     getAutoCreate : function(){
8991         
8992         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8993         
8994         var id = Roo.id();
8995         
8996         var cfg = {};
8997         
8998         var input =  {
8999             tag: 'textarea',
9000             id : id,
9001             warp : this.warp,
9002             rows : this.rows,
9003             value : this.value || '',
9004             html: this.html || '',
9005             cls : 'form-control',
9006             placeholder : this.placeholder || '' 
9007             
9008         };
9009         
9010         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9011             input.maxLength = this.maxLength;
9012         }
9013         
9014         if(this.resize){
9015             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9016         }
9017         
9018         if(this.cols){
9019             input.cols = this.cols;
9020         }
9021         
9022         if (this.readOnly) {
9023             input.readonly = true;
9024         }
9025         
9026         if (this.name) {
9027             input.name = this.name;
9028         }
9029         
9030         if (this.size) {
9031             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9032         }
9033         
9034         var settings=this;
9035         ['xs','sm','md','lg'].map(function(size){
9036             if (settings[size]) {
9037                 cfg.cls += ' col-' + size + '-' + settings[size];
9038             }
9039         });
9040         
9041         var inputblock = input;
9042         
9043         if(this.hasFeedback && !this.allowBlank){
9044             
9045             var feedback = {
9046                 tag: 'span',
9047                 cls: 'glyphicon form-control-feedback'
9048             };
9049
9050             inputblock = {
9051                 cls : 'has-feedback',
9052                 cn :  [
9053                     input,
9054                     feedback
9055                 ] 
9056             };  
9057         }
9058         
9059         
9060         if (this.before || this.after) {
9061             
9062             inputblock = {
9063                 cls : 'input-group',
9064                 cn :  [] 
9065             };
9066             if (this.before) {
9067                 inputblock.cn.push({
9068                     tag :'span',
9069                     cls : 'input-group-addon',
9070                     html : this.before
9071                 });
9072             }
9073             
9074             inputblock.cn.push(input);
9075             
9076             if(this.hasFeedback && !this.allowBlank){
9077                 inputblock.cls += ' has-feedback';
9078                 inputblock.cn.push(feedback);
9079             }
9080             
9081             if (this.after) {
9082                 inputblock.cn.push({
9083                     tag :'span',
9084                     cls : 'input-group-addon',
9085                     html : this.after
9086                 });
9087             }
9088             
9089         }
9090         
9091         if (align ==='left' && this.fieldLabel.length) {
9092 //                Roo.log("left and has label");
9093                 cfg.cn = [
9094                     
9095                     {
9096                         tag: 'label',
9097                         'for' :  id,
9098                         cls : 'control-label col-sm-' + this.labelWidth,
9099                         html : this.fieldLabel
9100                         
9101                     },
9102                     {
9103                         cls : "col-sm-" + (12 - this.labelWidth), 
9104                         cn: [
9105                             inputblock
9106                         ]
9107                     }
9108                     
9109                 ];
9110         } else if ( this.fieldLabel.length) {
9111 //                Roo.log(" label");
9112                  cfg.cn = [
9113                    
9114                     {
9115                         tag: 'label',
9116                         //cls : 'input-group-addon',
9117                         html : this.fieldLabel
9118                         
9119                     },
9120                     
9121                     inputblock
9122                     
9123                 ];
9124
9125         } else {
9126             
9127 //                   Roo.log(" no label && no align");
9128                 cfg.cn = [
9129                     
9130                         inputblock
9131                     
9132                 ];
9133                 
9134                 
9135         }
9136         
9137         if (this.disabled) {
9138             input.disabled=true;
9139         }
9140         
9141         return cfg;
9142         
9143     },
9144     /**
9145      * return the real textarea element.
9146      */
9147     inputEl: function ()
9148     {
9149         return this.el.select('textarea.form-control',true).first();
9150     },
9151     
9152     /**
9153      * Clear any invalid styles/messages for this field
9154      */
9155     clearInvalid : function()
9156     {
9157         
9158         if(!this.el || this.preventMark){ // not rendered
9159             return;
9160         }
9161         
9162         var label = this.el.select('label', true).first();
9163         var icon = this.el.select('i.fa-star', true).first();
9164         
9165         if(label && icon){
9166             icon.remove();
9167         }
9168         
9169         this.el.removeClass(this.invalidClass);
9170         
9171         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9172             
9173             var feedback = this.el.select('.form-control-feedback', true).first();
9174             
9175             if(feedback){
9176                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9177             }
9178             
9179         }
9180         
9181         this.fireEvent('valid', this);
9182     },
9183     
9184      /**
9185      * Mark this field as valid
9186      */
9187     markValid : function()
9188     {
9189         if(!this.el  || this.preventMark){ // not rendered
9190             return;
9191         }
9192         
9193         this.el.removeClass([this.invalidClass, this.validClass]);
9194         
9195         var feedback = this.el.select('.form-control-feedback', true).first();
9196             
9197         if(feedback){
9198             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9199         }
9200
9201         if(this.disabled || this.allowBlank){
9202             return;
9203         }
9204         
9205         var label = this.el.select('label', true).first();
9206         var icon = this.el.select('i.fa-star', true).first();
9207         
9208         if(label && icon){
9209             icon.remove();
9210         }
9211         
9212         this.el.addClass(this.validClass);
9213         
9214         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9215             
9216             var feedback = this.el.select('.form-control-feedback', true).first();
9217             
9218             if(feedback){
9219                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9220                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9221             }
9222             
9223         }
9224         
9225         this.fireEvent('valid', this);
9226     },
9227     
9228      /**
9229      * Mark this field as invalid
9230      * @param {String} msg The validation message
9231      */
9232     markInvalid : function(msg)
9233     {
9234         if(!this.el  || this.preventMark){ // not rendered
9235             return;
9236         }
9237         
9238         this.el.removeClass([this.invalidClass, this.validClass]);
9239         
9240         var feedback = this.el.select('.form-control-feedback', true).first();
9241             
9242         if(feedback){
9243             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9244         }
9245
9246         if(this.disabled || this.allowBlank){
9247             return;
9248         }
9249         
9250         var label = this.el.select('label', true).first();
9251         var icon = this.el.select('i.fa-star', true).first();
9252         
9253         if(!this.getValue().length && label && !icon){
9254             this.el.createChild({
9255                 tag : 'i',
9256                 cls : 'text-danger fa fa-lg fa-star',
9257                 tooltip : 'This field is required',
9258                 style : 'margin-right:5px;'
9259             }, label, true);
9260         }
9261
9262         this.el.addClass(this.invalidClass);
9263         
9264         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9265             
9266             var feedback = this.el.select('.form-control-feedback', true).first();
9267             
9268             if(feedback){
9269                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9270                 
9271                 if(this.getValue().length || this.forceFeedback){
9272                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9273                 }
9274                 
9275             }
9276             
9277         }
9278         
9279         this.fireEvent('invalid', this, msg);
9280     }
9281 });
9282
9283  
9284 /*
9285  * - LGPL
9286  *
9287  * trigger field - base class for combo..
9288  * 
9289  */
9290  
9291 /**
9292  * @class Roo.bootstrap.TriggerField
9293  * @extends Roo.bootstrap.Input
9294  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9295  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9296  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9297  * for which you can provide a custom implementation.  For example:
9298  * <pre><code>
9299 var trigger = new Roo.bootstrap.TriggerField();
9300 trigger.onTriggerClick = myTriggerFn;
9301 trigger.applyTo('my-field');
9302 </code></pre>
9303  *
9304  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9305  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9306  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9307  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9308  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9309
9310  * @constructor
9311  * Create a new TriggerField.
9312  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9313  * to the base TextField)
9314  */
9315 Roo.bootstrap.TriggerField = function(config){
9316     this.mimicing = false;
9317     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9318 };
9319
9320 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9321     /**
9322      * @cfg {String} triggerClass A CSS class to apply to the trigger
9323      */
9324      /**
9325      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9326      */
9327     hideTrigger:false,
9328
9329     /**
9330      * @cfg {Boolean} removable (true|false) special filter default false
9331      */
9332     removable : false,
9333     
9334     /** @cfg {Boolean} grow @hide */
9335     /** @cfg {Number} growMin @hide */
9336     /** @cfg {Number} growMax @hide */
9337
9338     /**
9339      * @hide 
9340      * @method
9341      */
9342     autoSize: Roo.emptyFn,
9343     // private
9344     monitorTab : true,
9345     // private
9346     deferHeight : true,
9347
9348     
9349     actionMode : 'wrap',
9350     
9351     caret : false,
9352     
9353     
9354     getAutoCreate : function(){
9355        
9356         var align = this.labelAlign || this.parentLabelAlign();
9357         
9358         var id = Roo.id();
9359         
9360         var cfg = {
9361             cls: 'form-group' //input-group
9362         };
9363         
9364         
9365         var input =  {
9366             tag: 'input',
9367             id : id,
9368             type : this.inputType,
9369             cls : 'form-control',
9370             autocomplete: 'new-password',
9371             placeholder : this.placeholder || '' 
9372             
9373         };
9374         if (this.name) {
9375             input.name = this.name;
9376         }
9377         if (this.size) {
9378             input.cls += ' input-' + this.size;
9379         }
9380         
9381         if (this.disabled) {
9382             input.disabled=true;
9383         }
9384         
9385         var inputblock = input;
9386         
9387         if(this.hasFeedback && !this.allowBlank){
9388             
9389             var feedback = {
9390                 tag: 'span',
9391                 cls: 'glyphicon form-control-feedback'
9392             };
9393             
9394             if(this.removable && !this.editable && !this.tickable){
9395                 inputblock = {
9396                     cls : 'has-feedback',
9397                     cn :  [
9398                         inputblock,
9399                         {
9400                             tag: 'button',
9401                             html : 'x',
9402                             cls : 'roo-combo-removable-btn close'
9403                         },
9404                         feedback
9405                     ] 
9406                 };
9407             } else {
9408                 inputblock = {
9409                     cls : 'has-feedback',
9410                     cn :  [
9411                         inputblock,
9412                         feedback
9413                     ] 
9414                 };
9415             }
9416
9417         } else {
9418             if(this.removable && !this.editable && !this.tickable){
9419                 inputblock = {
9420                     cls : 'roo-removable',
9421                     cn :  [
9422                         inputblock,
9423                         {
9424                             tag: 'button',
9425                             html : 'x',
9426                             cls : 'roo-combo-removable-btn close'
9427                         }
9428                     ] 
9429                 };
9430             }
9431         }
9432         
9433         if (this.before || this.after) {
9434             
9435             inputblock = {
9436                 cls : 'input-group',
9437                 cn :  [] 
9438             };
9439             if (this.before) {
9440                 inputblock.cn.push({
9441                     tag :'span',
9442                     cls : 'input-group-addon',
9443                     html : this.before
9444                 });
9445             }
9446             
9447             inputblock.cn.push(input);
9448             
9449             if(this.hasFeedback && !this.allowBlank){
9450                 inputblock.cls += ' has-feedback';
9451                 inputblock.cn.push(feedback);
9452             }
9453             
9454             if (this.after) {
9455                 inputblock.cn.push({
9456                     tag :'span',
9457                     cls : 'input-group-addon',
9458                     html : this.after
9459                 });
9460             }
9461             
9462         };
9463         
9464         var box = {
9465             tag: 'div',
9466             cn: [
9467                 {
9468                     tag: 'input',
9469                     type : 'hidden',
9470                     cls: 'form-hidden-field'
9471                 },
9472                 inputblock
9473             ]
9474             
9475         };
9476         
9477         if(this.multiple){
9478             box = {
9479                 tag: 'div',
9480                 cn: [
9481                     {
9482                         tag: 'input',
9483                         type : 'hidden',
9484                         cls: 'form-hidden-field'
9485                     },
9486                     {
9487                         tag: 'ul',
9488                         cls: 'roo-select2-choices',
9489                         cn:[
9490                             {
9491                                 tag: 'li',
9492                                 cls: 'roo-select2-search-field',
9493                                 cn: [
9494
9495                                     inputblock
9496                                 ]
9497                             }
9498                         ]
9499                     }
9500                 ]
9501             }
9502         };
9503         
9504         var combobox = {
9505             cls: 'roo-select2-container input-group',
9506             cn: [
9507                 box
9508 //                {
9509 //                    tag: 'ul',
9510 //                    cls: 'typeahead typeahead-long dropdown-menu',
9511 //                    style: 'display:none'
9512 //                }
9513             ]
9514         };
9515         
9516         if(!this.multiple && this.showToggleBtn){
9517             
9518             var caret = {
9519                         tag: 'span',
9520                         cls: 'caret'
9521              };
9522             if (this.caret != false) {
9523                 caret = {
9524                      tag: 'i',
9525                      cls: 'fa fa-' + this.caret
9526                 };
9527                 
9528             }
9529             
9530             combobox.cn.push({
9531                 tag :'span',
9532                 cls : 'input-group-addon btn dropdown-toggle',
9533                 cn : [
9534                     caret,
9535                     {
9536                         tag: 'span',
9537                         cls: 'combobox-clear',
9538                         cn  : [
9539                             {
9540                                 tag : 'i',
9541                                 cls: 'icon-remove'
9542                             }
9543                         ]
9544                     }
9545                 ]
9546
9547             })
9548         }
9549         
9550         if(this.multiple){
9551             combobox.cls += ' roo-select2-container-multi';
9552         }
9553         
9554         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9555             
9556 //                Roo.log("left and has label");
9557             cfg.cn = [
9558                 {
9559                     tag : 'i',
9560                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9561                     tooltip : 'This field is required'
9562                 },
9563                 {
9564                     tag: 'label',
9565                     'for' :  id,
9566                     cls : 'control-label col-sm-' + this.labelWidth,
9567                     html : this.fieldLabel
9568
9569                 },
9570                 {
9571                     cls : "col-sm-" + (12 - this.labelWidth), 
9572                     cn: [
9573                         combobox
9574                     ]
9575                 }
9576
9577             ];
9578             
9579             if(this.indicatorpos == 'right'){
9580                 cfg.cn = [
9581                     {
9582                         tag: 'label',
9583                         'for' :  id,
9584                         cls : 'control-label col-sm-' + this.labelWidth,
9585                         html : this.fieldLabel
9586
9587                     },
9588                     {
9589                         tag : 'i',
9590                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9591                         tooltip : 'This field is required'
9592                     },
9593                     {
9594                         cls : "col-sm-" + (12 - this.labelWidth), 
9595                         cn: [
9596                             combobox
9597                         ]
9598                     }
9599
9600                 ];
9601             }
9602             
9603         } else if ( this.fieldLabel.length) {
9604 //                Roo.log(" label");
9605             cfg.cn = [
9606                 {
9607                    tag : 'i',
9608                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9609                    tooltip : 'This field is required'
9610                },
9611                {
9612                    tag: 'label',
9613                    //cls : 'input-group-addon',
9614                    html : this.fieldLabel
9615
9616                },
9617
9618                combobox
9619
9620             ];
9621             
9622             if(this.indicatorpos == 'right'){
9623                 
9624                 cfg.cn = [
9625                     {
9626                        tag: 'label',
9627                        //cls : 'input-group-addon',
9628                        html : this.fieldLabel
9629
9630                     },
9631                     {
9632                        tag : 'i',
9633                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9634                        tooltip : 'This field is required'
9635                     },
9636                     
9637                     combobox
9638
9639                 ];
9640
9641             }
9642
9643         } else {
9644             
9645 //                Roo.log(" no label && no align");
9646                 cfg = combobox
9647                      
9648                 
9649         }
9650          
9651         var settings=this;
9652         ['xs','sm','md','lg'].map(function(size){
9653             if (settings[size]) {
9654                 cfg.cls += ' col-' + size + '-' + settings[size];
9655             }
9656         });
9657         
9658         return cfg;
9659         
9660     },
9661     
9662     
9663     
9664     // private
9665     onResize : function(w, h){
9666 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9667 //        if(typeof w == 'number'){
9668 //            var x = w - this.trigger.getWidth();
9669 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9670 //            this.trigger.setStyle('left', x+'px');
9671 //        }
9672     },
9673
9674     // private
9675     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9676
9677     // private
9678     getResizeEl : function(){
9679         return this.inputEl();
9680     },
9681
9682     // private
9683     getPositionEl : function(){
9684         return this.inputEl();
9685     },
9686
9687     // private
9688     alignErrorIcon : function(){
9689         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9690     },
9691
9692     // private
9693     initEvents : function(){
9694         
9695         this.createList();
9696         
9697         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9698         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9699         if(!this.multiple && this.showToggleBtn){
9700             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9701             if(this.hideTrigger){
9702                 this.trigger.setDisplayed(false);
9703             }
9704             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9705         }
9706         
9707         if(this.multiple){
9708             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9709         }
9710         
9711         if(this.removable && !this.editable && !this.tickable){
9712             var close = this.closeTriggerEl();
9713             
9714             if(close){
9715                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9716                 close.on('click', this.removeBtnClick, this, close);
9717             }
9718         }
9719         
9720         //this.trigger.addClassOnOver('x-form-trigger-over');
9721         //this.trigger.addClassOnClick('x-form-trigger-click');
9722         
9723         //if(!this.width){
9724         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9725         //}
9726     },
9727     
9728     closeTriggerEl : function()
9729     {
9730         var close = this.el.select('.roo-combo-removable-btn', true).first();
9731         return close ? close : false;
9732     },
9733     
9734     removeBtnClick : function(e, h, el)
9735     {
9736         e.preventDefault();
9737         
9738         if(this.fireEvent("remove", this) !== false){
9739             this.reset();
9740             this.fireEvent("afterremove", this)
9741         }
9742     },
9743     
9744     createList : function()
9745     {
9746         this.list = Roo.get(document.body).createChild({
9747             tag: 'ul',
9748             cls: 'typeahead typeahead-long dropdown-menu',
9749             style: 'display:none'
9750         });
9751         
9752         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9753         
9754     },
9755
9756     // private
9757     initTrigger : function(){
9758        
9759     },
9760
9761     // private
9762     onDestroy : function(){
9763         if(this.trigger){
9764             this.trigger.removeAllListeners();
9765           //  this.trigger.remove();
9766         }
9767         //if(this.wrap){
9768         //    this.wrap.remove();
9769         //}
9770         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9771     },
9772
9773     // private
9774     onFocus : function(){
9775         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9776         /*
9777         if(!this.mimicing){
9778             this.wrap.addClass('x-trigger-wrap-focus');
9779             this.mimicing = true;
9780             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9781             if(this.monitorTab){
9782                 this.el.on("keydown", this.checkTab, this);
9783             }
9784         }
9785         */
9786     },
9787
9788     // private
9789     checkTab : function(e){
9790         if(e.getKey() == e.TAB){
9791             this.triggerBlur();
9792         }
9793     },
9794
9795     // private
9796     onBlur : function(){
9797         // do nothing
9798     },
9799
9800     // private
9801     mimicBlur : function(e, t){
9802         /*
9803         if(!this.wrap.contains(t) && this.validateBlur()){
9804             this.triggerBlur();
9805         }
9806         */
9807     },
9808
9809     // private
9810     triggerBlur : function(){
9811         this.mimicing = false;
9812         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9813         if(this.monitorTab){
9814             this.el.un("keydown", this.checkTab, this);
9815         }
9816         //this.wrap.removeClass('x-trigger-wrap-focus');
9817         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9818     },
9819
9820     // private
9821     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9822     validateBlur : function(e, t){
9823         return true;
9824     },
9825
9826     // private
9827     onDisable : function(){
9828         this.inputEl().dom.disabled = true;
9829         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9830         //if(this.wrap){
9831         //    this.wrap.addClass('x-item-disabled');
9832         //}
9833     },
9834
9835     // private
9836     onEnable : function(){
9837         this.inputEl().dom.disabled = false;
9838         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9839         //if(this.wrap){
9840         //    this.el.removeClass('x-item-disabled');
9841         //}
9842     },
9843
9844     // private
9845     onShow : function(){
9846         var ae = this.getActionEl();
9847         
9848         if(ae){
9849             ae.dom.style.display = '';
9850             ae.dom.style.visibility = 'visible';
9851         }
9852     },
9853
9854     // private
9855     
9856     onHide : function(){
9857         var ae = this.getActionEl();
9858         ae.dom.style.display = 'none';
9859     },
9860
9861     /**
9862      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9863      * by an implementing function.
9864      * @method
9865      * @param {EventObject} e
9866      */
9867     onTriggerClick : Roo.emptyFn
9868 });
9869  /*
9870  * Based on:
9871  * Ext JS Library 1.1.1
9872  * Copyright(c) 2006-2007, Ext JS, LLC.
9873  *
9874  * Originally Released Under LGPL - original licence link has changed is not relivant.
9875  *
9876  * Fork - LGPL
9877  * <script type="text/javascript">
9878  */
9879
9880
9881 /**
9882  * @class Roo.data.SortTypes
9883  * @singleton
9884  * Defines the default sorting (casting?) comparison functions used when sorting data.
9885  */
9886 Roo.data.SortTypes = {
9887     /**
9888      * Default sort that does nothing
9889      * @param {Mixed} s The value being converted
9890      * @return {Mixed} The comparison value
9891      */
9892     none : function(s){
9893         return s;
9894     },
9895     
9896     /**
9897      * The regular expression used to strip tags
9898      * @type {RegExp}
9899      * @property
9900      */
9901     stripTagsRE : /<\/?[^>]+>/gi,
9902     
9903     /**
9904      * Strips all HTML tags to sort on text only
9905      * @param {Mixed} s The value being converted
9906      * @return {String} The comparison value
9907      */
9908     asText : function(s){
9909         return String(s).replace(this.stripTagsRE, "");
9910     },
9911     
9912     /**
9913      * Strips all HTML tags to sort on text only - Case insensitive
9914      * @param {Mixed} s The value being converted
9915      * @return {String} The comparison value
9916      */
9917     asUCText : function(s){
9918         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9919     },
9920     
9921     /**
9922      * Case insensitive string
9923      * @param {Mixed} s The value being converted
9924      * @return {String} The comparison value
9925      */
9926     asUCString : function(s) {
9927         return String(s).toUpperCase();
9928     },
9929     
9930     /**
9931      * Date sorting
9932      * @param {Mixed} s The value being converted
9933      * @return {Number} The comparison value
9934      */
9935     asDate : function(s) {
9936         if(!s){
9937             return 0;
9938         }
9939         if(s instanceof Date){
9940             return s.getTime();
9941         }
9942         return Date.parse(String(s));
9943     },
9944     
9945     /**
9946      * Float sorting
9947      * @param {Mixed} s The value being converted
9948      * @return {Float} The comparison value
9949      */
9950     asFloat : function(s) {
9951         var val = parseFloat(String(s).replace(/,/g, ""));
9952         if(isNaN(val)) {
9953             val = 0;
9954         }
9955         return val;
9956     },
9957     
9958     /**
9959      * Integer sorting
9960      * @param {Mixed} s The value being converted
9961      * @return {Number} The comparison value
9962      */
9963     asInt : function(s) {
9964         var val = parseInt(String(s).replace(/,/g, ""));
9965         if(isNaN(val)) {
9966             val = 0;
9967         }
9968         return val;
9969     }
9970 };/*
9971  * Based on:
9972  * Ext JS Library 1.1.1
9973  * Copyright(c) 2006-2007, Ext JS, LLC.
9974  *
9975  * Originally Released Under LGPL - original licence link has changed is not relivant.
9976  *
9977  * Fork - LGPL
9978  * <script type="text/javascript">
9979  */
9980
9981 /**
9982 * @class Roo.data.Record
9983  * Instances of this class encapsulate both record <em>definition</em> information, and record
9984  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9985  * to access Records cached in an {@link Roo.data.Store} object.<br>
9986  * <p>
9987  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9988  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9989  * objects.<br>
9990  * <p>
9991  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9992  * @constructor
9993  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9994  * {@link #create}. The parameters are the same.
9995  * @param {Array} data An associative Array of data values keyed by the field name.
9996  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9997  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9998  * not specified an integer id is generated.
9999  */
10000 Roo.data.Record = function(data, id){
10001     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10002     this.data = data;
10003 };
10004
10005 /**
10006  * Generate a constructor for a specific record layout.
10007  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10008  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10009  * Each field definition object may contain the following properties: <ul>
10010  * <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,
10011  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10012  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10013  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10014  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10015  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10016  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10017  * this may be omitted.</p></li>
10018  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10019  * <ul><li>auto (Default, implies no conversion)</li>
10020  * <li>string</li>
10021  * <li>int</li>
10022  * <li>float</li>
10023  * <li>boolean</li>
10024  * <li>date</li></ul></p></li>
10025  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10026  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10027  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10028  * by the Reader into an object that will be stored in the Record. It is passed the
10029  * following parameters:<ul>
10030  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10031  * </ul></p></li>
10032  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10033  * </ul>
10034  * <br>usage:<br><pre><code>
10035 var TopicRecord = Roo.data.Record.create(
10036     {name: 'title', mapping: 'topic_title'},
10037     {name: 'author', mapping: 'username'},
10038     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10039     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10040     {name: 'lastPoster', mapping: 'user2'},
10041     {name: 'excerpt', mapping: 'post_text'}
10042 );
10043
10044 var myNewRecord = new TopicRecord({
10045     title: 'Do my job please',
10046     author: 'noobie',
10047     totalPosts: 1,
10048     lastPost: new Date(),
10049     lastPoster: 'Animal',
10050     excerpt: 'No way dude!'
10051 });
10052 myStore.add(myNewRecord);
10053 </code></pre>
10054  * @method create
10055  * @static
10056  */
10057 Roo.data.Record.create = function(o){
10058     var f = function(){
10059         f.superclass.constructor.apply(this, arguments);
10060     };
10061     Roo.extend(f, Roo.data.Record);
10062     var p = f.prototype;
10063     p.fields = new Roo.util.MixedCollection(false, function(field){
10064         return field.name;
10065     });
10066     for(var i = 0, len = o.length; i < len; i++){
10067         p.fields.add(new Roo.data.Field(o[i]));
10068     }
10069     f.getField = function(name){
10070         return p.fields.get(name);  
10071     };
10072     return f;
10073 };
10074
10075 Roo.data.Record.AUTO_ID = 1000;
10076 Roo.data.Record.EDIT = 'edit';
10077 Roo.data.Record.REJECT = 'reject';
10078 Roo.data.Record.COMMIT = 'commit';
10079
10080 Roo.data.Record.prototype = {
10081     /**
10082      * Readonly flag - true if this record has been modified.
10083      * @type Boolean
10084      */
10085     dirty : false,
10086     editing : false,
10087     error: null,
10088     modified: null,
10089
10090     // private
10091     join : function(store){
10092         this.store = store;
10093     },
10094
10095     /**
10096      * Set the named field to the specified value.
10097      * @param {String} name The name of the field to set.
10098      * @param {Object} value The value to set the field to.
10099      */
10100     set : function(name, value){
10101         if(this.data[name] == value){
10102             return;
10103         }
10104         this.dirty = true;
10105         if(!this.modified){
10106             this.modified = {};
10107         }
10108         if(typeof this.modified[name] == 'undefined'){
10109             this.modified[name] = this.data[name];
10110         }
10111         this.data[name] = value;
10112         if(!this.editing && this.store){
10113             this.store.afterEdit(this);
10114         }       
10115     },
10116
10117     /**
10118      * Get the value of the named field.
10119      * @param {String} name The name of the field to get the value of.
10120      * @return {Object} The value of the field.
10121      */
10122     get : function(name){
10123         return this.data[name]; 
10124     },
10125
10126     // private
10127     beginEdit : function(){
10128         this.editing = true;
10129         this.modified = {}; 
10130     },
10131
10132     // private
10133     cancelEdit : function(){
10134         this.editing = false;
10135         delete this.modified;
10136     },
10137
10138     // private
10139     endEdit : function(){
10140         this.editing = false;
10141         if(this.dirty && this.store){
10142             this.store.afterEdit(this);
10143         }
10144     },
10145
10146     /**
10147      * Usually called by the {@link Roo.data.Store} which owns the Record.
10148      * Rejects all changes made to the Record since either creation, or the last commit operation.
10149      * Modified fields are reverted to their original values.
10150      * <p>
10151      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10152      * of reject operations.
10153      */
10154     reject : function(){
10155         var m = this.modified;
10156         for(var n in m){
10157             if(typeof m[n] != "function"){
10158                 this.data[n] = m[n];
10159             }
10160         }
10161         this.dirty = false;
10162         delete this.modified;
10163         this.editing = false;
10164         if(this.store){
10165             this.store.afterReject(this);
10166         }
10167     },
10168
10169     /**
10170      * Usually called by the {@link Roo.data.Store} which owns the Record.
10171      * Commits all changes made to the Record since either creation, or the last commit operation.
10172      * <p>
10173      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10174      * of commit operations.
10175      */
10176     commit : function(){
10177         this.dirty = false;
10178         delete this.modified;
10179         this.editing = false;
10180         if(this.store){
10181             this.store.afterCommit(this);
10182         }
10183     },
10184
10185     // private
10186     hasError : function(){
10187         return this.error != null;
10188     },
10189
10190     // private
10191     clearError : function(){
10192         this.error = null;
10193     },
10194
10195     /**
10196      * Creates a copy of this record.
10197      * @param {String} id (optional) A new record id if you don't want to use this record's id
10198      * @return {Record}
10199      */
10200     copy : function(newId) {
10201         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10202     }
10203 };/*
10204  * Based on:
10205  * Ext JS Library 1.1.1
10206  * Copyright(c) 2006-2007, Ext JS, LLC.
10207  *
10208  * Originally Released Under LGPL - original licence link has changed is not relivant.
10209  *
10210  * Fork - LGPL
10211  * <script type="text/javascript">
10212  */
10213
10214
10215
10216 /**
10217  * @class Roo.data.Store
10218  * @extends Roo.util.Observable
10219  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10220  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10221  * <p>
10222  * 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
10223  * has no knowledge of the format of the data returned by the Proxy.<br>
10224  * <p>
10225  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10226  * instances from the data object. These records are cached and made available through accessor functions.
10227  * @constructor
10228  * Creates a new Store.
10229  * @param {Object} config A config object containing the objects needed for the Store to access data,
10230  * and read the data into Records.
10231  */
10232 Roo.data.Store = function(config){
10233     this.data = new Roo.util.MixedCollection(false);
10234     this.data.getKey = function(o){
10235         return o.id;
10236     };
10237     this.baseParams = {};
10238     // private
10239     this.paramNames = {
10240         "start" : "start",
10241         "limit" : "limit",
10242         "sort" : "sort",
10243         "dir" : "dir",
10244         "multisort" : "_multisort"
10245     };
10246
10247     if(config && config.data){
10248         this.inlineData = config.data;
10249         delete config.data;
10250     }
10251
10252     Roo.apply(this, config);
10253     
10254     if(this.reader){ // reader passed
10255         this.reader = Roo.factory(this.reader, Roo.data);
10256         this.reader.xmodule = this.xmodule || false;
10257         if(!this.recordType){
10258             this.recordType = this.reader.recordType;
10259         }
10260         if(this.reader.onMetaChange){
10261             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10262         }
10263     }
10264
10265     if(this.recordType){
10266         this.fields = this.recordType.prototype.fields;
10267     }
10268     this.modified = [];
10269
10270     this.addEvents({
10271         /**
10272          * @event datachanged
10273          * Fires when the data cache has changed, and a widget which is using this Store
10274          * as a Record cache should refresh its view.
10275          * @param {Store} this
10276          */
10277         datachanged : true,
10278         /**
10279          * @event metachange
10280          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10281          * @param {Store} this
10282          * @param {Object} meta The JSON metadata
10283          */
10284         metachange : true,
10285         /**
10286          * @event add
10287          * Fires when Records have been added to the Store
10288          * @param {Store} this
10289          * @param {Roo.data.Record[]} records The array of Records added
10290          * @param {Number} index The index at which the record(s) were added
10291          */
10292         add : true,
10293         /**
10294          * @event remove
10295          * Fires when a Record has been removed from the Store
10296          * @param {Store} this
10297          * @param {Roo.data.Record} record The Record that was removed
10298          * @param {Number} index The index at which the record was removed
10299          */
10300         remove : true,
10301         /**
10302          * @event update
10303          * Fires when a Record has been updated
10304          * @param {Store} this
10305          * @param {Roo.data.Record} record The Record that was updated
10306          * @param {String} operation The update operation being performed.  Value may be one of:
10307          * <pre><code>
10308  Roo.data.Record.EDIT
10309  Roo.data.Record.REJECT
10310  Roo.data.Record.COMMIT
10311          * </code></pre>
10312          */
10313         update : true,
10314         /**
10315          * @event clear
10316          * Fires when the data cache has been cleared.
10317          * @param {Store} this
10318          */
10319         clear : true,
10320         /**
10321          * @event beforeload
10322          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10323          * the load action will be canceled.
10324          * @param {Store} this
10325          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10326          */
10327         beforeload : true,
10328         /**
10329          * @event beforeloadadd
10330          * Fires after a new set of Records has been loaded.
10331          * @param {Store} this
10332          * @param {Roo.data.Record[]} records The Records that were loaded
10333          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10334          */
10335         beforeloadadd : true,
10336         /**
10337          * @event load
10338          * Fires after a new set of Records has been loaded, before they are added to the store.
10339          * @param {Store} this
10340          * @param {Roo.data.Record[]} records The Records that were loaded
10341          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10342          * @params {Object} return from reader
10343          */
10344         load : true,
10345         /**
10346          * @event loadexception
10347          * Fires if an exception occurs in the Proxy during loading.
10348          * Called with the signature of the Proxy's "loadexception" event.
10349          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10350          * 
10351          * @param {Proxy} 
10352          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10353          * @param {Object} load options 
10354          * @param {Object} jsonData from your request (normally this contains the Exception)
10355          */
10356         loadexception : true
10357     });
10358     
10359     if(this.proxy){
10360         this.proxy = Roo.factory(this.proxy, Roo.data);
10361         this.proxy.xmodule = this.xmodule || false;
10362         this.relayEvents(this.proxy,  ["loadexception"]);
10363     }
10364     this.sortToggle = {};
10365     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10366
10367     Roo.data.Store.superclass.constructor.call(this);
10368
10369     if(this.inlineData){
10370         this.loadData(this.inlineData);
10371         delete this.inlineData;
10372     }
10373 };
10374
10375 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10376      /**
10377     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10378     * without a remote query - used by combo/forms at present.
10379     */
10380     
10381     /**
10382     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10383     */
10384     /**
10385     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10386     */
10387     /**
10388     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10389     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10390     */
10391     /**
10392     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10393     * on any HTTP request
10394     */
10395     /**
10396     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10397     */
10398     /**
10399     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10400     */
10401     multiSort: false,
10402     /**
10403     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10404     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10405     */
10406     remoteSort : false,
10407
10408     /**
10409     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10410      * loaded or when a record is removed. (defaults to false).
10411     */
10412     pruneModifiedRecords : false,
10413
10414     // private
10415     lastOptions : null,
10416
10417     /**
10418      * Add Records to the Store and fires the add event.
10419      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10420      */
10421     add : function(records){
10422         records = [].concat(records);
10423         for(var i = 0, len = records.length; i < len; i++){
10424             records[i].join(this);
10425         }
10426         var index = this.data.length;
10427         this.data.addAll(records);
10428         this.fireEvent("add", this, records, index);
10429     },
10430
10431     /**
10432      * Remove a Record from the Store and fires the remove event.
10433      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10434      */
10435     remove : function(record){
10436         var index = this.data.indexOf(record);
10437         this.data.removeAt(index);
10438         if(this.pruneModifiedRecords){
10439             this.modified.remove(record);
10440         }
10441         this.fireEvent("remove", this, record, index);
10442     },
10443
10444     /**
10445      * Remove all Records from the Store and fires the clear event.
10446      */
10447     removeAll : function(){
10448         this.data.clear();
10449         if(this.pruneModifiedRecords){
10450             this.modified = [];
10451         }
10452         this.fireEvent("clear", this);
10453     },
10454
10455     /**
10456      * Inserts Records to the Store at the given index and fires the add event.
10457      * @param {Number} index The start index at which to insert the passed Records.
10458      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10459      */
10460     insert : function(index, records){
10461         records = [].concat(records);
10462         for(var i = 0, len = records.length; i < len; i++){
10463             this.data.insert(index, records[i]);
10464             records[i].join(this);
10465         }
10466         this.fireEvent("add", this, records, index);
10467     },
10468
10469     /**
10470      * Get the index within the cache of the passed Record.
10471      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10472      * @return {Number} The index of the passed Record. Returns -1 if not found.
10473      */
10474     indexOf : function(record){
10475         return this.data.indexOf(record);
10476     },
10477
10478     /**
10479      * Get the index within the cache of the Record with the passed id.
10480      * @param {String} id The id of the Record to find.
10481      * @return {Number} The index of the Record. Returns -1 if not found.
10482      */
10483     indexOfId : function(id){
10484         return this.data.indexOfKey(id);
10485     },
10486
10487     /**
10488      * Get the Record with the specified id.
10489      * @param {String} id The id of the Record to find.
10490      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10491      */
10492     getById : function(id){
10493         return this.data.key(id);
10494     },
10495
10496     /**
10497      * Get the Record at the specified index.
10498      * @param {Number} index The index of the Record to find.
10499      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10500      */
10501     getAt : function(index){
10502         return this.data.itemAt(index);
10503     },
10504
10505     /**
10506      * Returns a range of Records between specified indices.
10507      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10508      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10509      * @return {Roo.data.Record[]} An array of Records
10510      */
10511     getRange : function(start, end){
10512         return this.data.getRange(start, end);
10513     },
10514
10515     // private
10516     storeOptions : function(o){
10517         o = Roo.apply({}, o);
10518         delete o.callback;
10519         delete o.scope;
10520         this.lastOptions = o;
10521     },
10522
10523     /**
10524      * Loads the Record cache from the configured Proxy using the configured Reader.
10525      * <p>
10526      * If using remote paging, then the first load call must specify the <em>start</em>
10527      * and <em>limit</em> properties in the options.params property to establish the initial
10528      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10529      * <p>
10530      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10531      * and this call will return before the new data has been loaded. Perform any post-processing
10532      * in a callback function, or in a "load" event handler.</strong>
10533      * <p>
10534      * @param {Object} options An object containing properties which control loading options:<ul>
10535      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10536      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10537      * passed the following arguments:<ul>
10538      * <li>r : Roo.data.Record[]</li>
10539      * <li>options: Options object from the load call</li>
10540      * <li>success: Boolean success indicator</li></ul></li>
10541      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10542      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10543      * </ul>
10544      */
10545     load : function(options){
10546         options = options || {};
10547         if(this.fireEvent("beforeload", this, options) !== false){
10548             this.storeOptions(options);
10549             var p = Roo.apply(options.params || {}, this.baseParams);
10550             // if meta was not loaded from remote source.. try requesting it.
10551             if (!this.reader.metaFromRemote) {
10552                 p._requestMeta = 1;
10553             }
10554             if(this.sortInfo && this.remoteSort){
10555                 var pn = this.paramNames;
10556                 p[pn["sort"]] = this.sortInfo.field;
10557                 p[pn["dir"]] = this.sortInfo.direction;
10558             }
10559             if (this.multiSort) {
10560                 var pn = this.paramNames;
10561                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10562             }
10563             
10564             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10565         }
10566     },
10567
10568     /**
10569      * Reloads the Record cache from the configured Proxy using the configured Reader and
10570      * the options from the last load operation performed.
10571      * @param {Object} options (optional) An object containing properties which may override the options
10572      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10573      * the most recently used options are reused).
10574      */
10575     reload : function(options){
10576         this.load(Roo.applyIf(options||{}, this.lastOptions));
10577     },
10578
10579     // private
10580     // Called as a callback by the Reader during a load operation.
10581     loadRecords : function(o, options, success){
10582         if(!o || success === false){
10583             if(success !== false){
10584                 this.fireEvent("load", this, [], options, o);
10585             }
10586             if(options.callback){
10587                 options.callback.call(options.scope || this, [], options, false);
10588             }
10589             return;
10590         }
10591         // if data returned failure - throw an exception.
10592         if (o.success === false) {
10593             // show a message if no listener is registered.
10594             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10595                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10596             }
10597             // loadmask wil be hooked into this..
10598             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10599             return;
10600         }
10601         var r = o.records, t = o.totalRecords || r.length;
10602         
10603         this.fireEvent("beforeloadadd", this, r, options, o);
10604         
10605         if(!options || options.add !== true){
10606             if(this.pruneModifiedRecords){
10607                 this.modified = [];
10608             }
10609             for(var i = 0, len = r.length; i < len; i++){
10610                 r[i].join(this);
10611             }
10612             if(this.snapshot){
10613                 this.data = this.snapshot;
10614                 delete this.snapshot;
10615             }
10616             this.data.clear();
10617             this.data.addAll(r);
10618             this.totalLength = t;
10619             this.applySort();
10620             this.fireEvent("datachanged", this);
10621         }else{
10622             this.totalLength = Math.max(t, this.data.length+r.length);
10623             this.add(r);
10624         }
10625         this.fireEvent("load", this, r, options, o);
10626         if(options.callback){
10627             options.callback.call(options.scope || this, r, options, true);
10628         }
10629     },
10630
10631
10632     /**
10633      * Loads data from a passed data block. A Reader which understands the format of the data
10634      * must have been configured in the constructor.
10635      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10636      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10637      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10638      */
10639     loadData : function(o, append){
10640         var r = this.reader.readRecords(o);
10641         this.loadRecords(r, {add: append}, true);
10642     },
10643
10644     /**
10645      * Gets the number of cached records.
10646      * <p>
10647      * <em>If using paging, this may not be the total size of the dataset. If the data object
10648      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10649      * the data set size</em>
10650      */
10651     getCount : function(){
10652         return this.data.length || 0;
10653     },
10654
10655     /**
10656      * Gets the total number of records in the dataset as returned by the server.
10657      * <p>
10658      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10659      * the dataset size</em>
10660      */
10661     getTotalCount : function(){
10662         return this.totalLength || 0;
10663     },
10664
10665     /**
10666      * Returns the sort state of the Store as an object with two properties:
10667      * <pre><code>
10668  field {String} The name of the field by which the Records are sorted
10669  direction {String} The sort order, "ASC" or "DESC"
10670      * </code></pre>
10671      */
10672     getSortState : function(){
10673         return this.sortInfo;
10674     },
10675
10676     // private
10677     applySort : function(){
10678         if(this.sortInfo && !this.remoteSort){
10679             var s = this.sortInfo, f = s.field;
10680             var st = this.fields.get(f).sortType;
10681             var fn = function(r1, r2){
10682                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10683                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10684             };
10685             this.data.sort(s.direction, fn);
10686             if(this.snapshot && this.snapshot != this.data){
10687                 this.snapshot.sort(s.direction, fn);
10688             }
10689         }
10690     },
10691
10692     /**
10693      * Sets the default sort column and order to be used by the next load operation.
10694      * @param {String} fieldName The name of the field to sort by.
10695      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10696      */
10697     setDefaultSort : function(field, dir){
10698         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10699     },
10700
10701     /**
10702      * Sort the Records.
10703      * If remote sorting is used, the sort is performed on the server, and the cache is
10704      * reloaded. If local sorting is used, the cache is sorted internally.
10705      * @param {String} fieldName The name of the field to sort by.
10706      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10707      */
10708     sort : function(fieldName, dir){
10709         var f = this.fields.get(fieldName);
10710         if(!dir){
10711             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10712             
10713             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10714                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10715             }else{
10716                 dir = f.sortDir;
10717             }
10718         }
10719         this.sortToggle[f.name] = dir;
10720         this.sortInfo = {field: f.name, direction: dir};
10721         if(!this.remoteSort){
10722             this.applySort();
10723             this.fireEvent("datachanged", this);
10724         }else{
10725             this.load(this.lastOptions);
10726         }
10727     },
10728
10729     /**
10730      * Calls the specified function for each of the Records in the cache.
10731      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10732      * Returning <em>false</em> aborts and exits the iteration.
10733      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10734      */
10735     each : function(fn, scope){
10736         this.data.each(fn, scope);
10737     },
10738
10739     /**
10740      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10741      * (e.g., during paging).
10742      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10743      */
10744     getModifiedRecords : function(){
10745         return this.modified;
10746     },
10747
10748     // private
10749     createFilterFn : function(property, value, anyMatch){
10750         if(!value.exec){ // not a regex
10751             value = String(value);
10752             if(value.length == 0){
10753                 return false;
10754             }
10755             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10756         }
10757         return function(r){
10758             return value.test(r.data[property]);
10759         };
10760     },
10761
10762     /**
10763      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10764      * @param {String} property A field on your records
10765      * @param {Number} start The record index to start at (defaults to 0)
10766      * @param {Number} end The last record index to include (defaults to length - 1)
10767      * @return {Number} The sum
10768      */
10769     sum : function(property, start, end){
10770         var rs = this.data.items, v = 0;
10771         start = start || 0;
10772         end = (end || end === 0) ? end : rs.length-1;
10773
10774         for(var i = start; i <= end; i++){
10775             v += (rs[i].data[property] || 0);
10776         }
10777         return v;
10778     },
10779
10780     /**
10781      * Filter the records by a specified property.
10782      * @param {String} field A field on your records
10783      * @param {String/RegExp} value Either a string that the field
10784      * should start with or a RegExp to test against the field
10785      * @param {Boolean} anyMatch True to match any part not just the beginning
10786      */
10787     filter : function(property, value, anyMatch){
10788         var fn = this.createFilterFn(property, value, anyMatch);
10789         return fn ? this.filterBy(fn) : this.clearFilter();
10790     },
10791
10792     /**
10793      * Filter by a function. The specified function will be called with each
10794      * record in this data source. If the function returns true the record is included,
10795      * otherwise it is filtered.
10796      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10797      * @param {Object} scope (optional) The scope of the function (defaults to this)
10798      */
10799     filterBy : function(fn, scope){
10800         this.snapshot = this.snapshot || this.data;
10801         this.data = this.queryBy(fn, scope||this);
10802         this.fireEvent("datachanged", this);
10803     },
10804
10805     /**
10806      * Query the records by a specified property.
10807      * @param {String} field A field on your records
10808      * @param {String/RegExp} value Either a string that the field
10809      * should start with or a RegExp to test against the field
10810      * @param {Boolean} anyMatch True to match any part not just the beginning
10811      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10812      */
10813     query : function(property, value, anyMatch){
10814         var fn = this.createFilterFn(property, value, anyMatch);
10815         return fn ? this.queryBy(fn) : this.data.clone();
10816     },
10817
10818     /**
10819      * Query by a function. The specified function will be called with each
10820      * record in this data source. If the function returns true the record is included
10821      * in the results.
10822      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10823      * @param {Object} scope (optional) The scope of the function (defaults to this)
10824       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10825      **/
10826     queryBy : function(fn, scope){
10827         var data = this.snapshot || this.data;
10828         return data.filterBy(fn, scope||this);
10829     },
10830
10831     /**
10832      * Collects unique values for a particular dataIndex from this store.
10833      * @param {String} dataIndex The property to collect
10834      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10835      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10836      * @return {Array} An array of the unique values
10837      **/
10838     collect : function(dataIndex, allowNull, bypassFilter){
10839         var d = (bypassFilter === true && this.snapshot) ?
10840                 this.snapshot.items : this.data.items;
10841         var v, sv, r = [], l = {};
10842         for(var i = 0, len = d.length; i < len; i++){
10843             v = d[i].data[dataIndex];
10844             sv = String(v);
10845             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10846                 l[sv] = true;
10847                 r[r.length] = v;
10848             }
10849         }
10850         return r;
10851     },
10852
10853     /**
10854      * Revert to a view of the Record cache with no filtering applied.
10855      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10856      */
10857     clearFilter : function(suppressEvent){
10858         if(this.snapshot && this.snapshot != this.data){
10859             this.data = this.snapshot;
10860             delete this.snapshot;
10861             if(suppressEvent !== true){
10862                 this.fireEvent("datachanged", this);
10863             }
10864         }
10865     },
10866
10867     // private
10868     afterEdit : function(record){
10869         if(this.modified.indexOf(record) == -1){
10870             this.modified.push(record);
10871         }
10872         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10873     },
10874     
10875     // private
10876     afterReject : function(record){
10877         this.modified.remove(record);
10878         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10879     },
10880
10881     // private
10882     afterCommit : function(record){
10883         this.modified.remove(record);
10884         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10885     },
10886
10887     /**
10888      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10889      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10890      */
10891     commitChanges : function(){
10892         var m = this.modified.slice(0);
10893         this.modified = [];
10894         for(var i = 0, len = m.length; i < len; i++){
10895             m[i].commit();
10896         }
10897     },
10898
10899     /**
10900      * Cancel outstanding changes on all changed records.
10901      */
10902     rejectChanges : function(){
10903         var m = this.modified.slice(0);
10904         this.modified = [];
10905         for(var i = 0, len = m.length; i < len; i++){
10906             m[i].reject();
10907         }
10908     },
10909
10910     onMetaChange : function(meta, rtype, o){
10911         this.recordType = rtype;
10912         this.fields = rtype.prototype.fields;
10913         delete this.snapshot;
10914         this.sortInfo = meta.sortInfo || this.sortInfo;
10915         this.modified = [];
10916         this.fireEvent('metachange', this, this.reader.meta);
10917     },
10918     
10919     moveIndex : function(data, type)
10920     {
10921         var index = this.indexOf(data);
10922         
10923         var newIndex = index + type;
10924         
10925         this.remove(data);
10926         
10927         this.insert(newIndex, data);
10928         
10929     }
10930 });/*
10931  * Based on:
10932  * Ext JS Library 1.1.1
10933  * Copyright(c) 2006-2007, Ext JS, LLC.
10934  *
10935  * Originally Released Under LGPL - original licence link has changed is not relivant.
10936  *
10937  * Fork - LGPL
10938  * <script type="text/javascript">
10939  */
10940
10941 /**
10942  * @class Roo.data.SimpleStore
10943  * @extends Roo.data.Store
10944  * Small helper class to make creating Stores from Array data easier.
10945  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10946  * @cfg {Array} fields An array of field definition objects, or field name strings.
10947  * @cfg {Array} data The multi-dimensional array of data
10948  * @constructor
10949  * @param {Object} config
10950  */
10951 Roo.data.SimpleStore = function(config){
10952     Roo.data.SimpleStore.superclass.constructor.call(this, {
10953         isLocal : true,
10954         reader: new Roo.data.ArrayReader({
10955                 id: config.id
10956             },
10957             Roo.data.Record.create(config.fields)
10958         ),
10959         proxy : new Roo.data.MemoryProxy(config.data)
10960     });
10961     this.load();
10962 };
10963 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10964  * Based on:
10965  * Ext JS Library 1.1.1
10966  * Copyright(c) 2006-2007, Ext JS, LLC.
10967  *
10968  * Originally Released Under LGPL - original licence link has changed is not relivant.
10969  *
10970  * Fork - LGPL
10971  * <script type="text/javascript">
10972  */
10973
10974 /**
10975 /**
10976  * @extends Roo.data.Store
10977  * @class Roo.data.JsonStore
10978  * Small helper class to make creating Stores for JSON data easier. <br/>
10979 <pre><code>
10980 var store = new Roo.data.JsonStore({
10981     url: 'get-images.php',
10982     root: 'images',
10983     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10984 });
10985 </code></pre>
10986  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10987  * JsonReader and HttpProxy (unless inline data is provided).</b>
10988  * @cfg {Array} fields An array of field definition objects, or field name strings.
10989  * @constructor
10990  * @param {Object} config
10991  */
10992 Roo.data.JsonStore = function(c){
10993     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10994         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10995         reader: new Roo.data.JsonReader(c, c.fields)
10996     }));
10997 };
10998 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10999  * Based on:
11000  * Ext JS Library 1.1.1
11001  * Copyright(c) 2006-2007, Ext JS, LLC.
11002  *
11003  * Originally Released Under LGPL - original licence link has changed is not relivant.
11004  *
11005  * Fork - LGPL
11006  * <script type="text/javascript">
11007  */
11008
11009  
11010 Roo.data.Field = function(config){
11011     if(typeof config == "string"){
11012         config = {name: config};
11013     }
11014     Roo.apply(this, config);
11015     
11016     if(!this.type){
11017         this.type = "auto";
11018     }
11019     
11020     var st = Roo.data.SortTypes;
11021     // named sortTypes are supported, here we look them up
11022     if(typeof this.sortType == "string"){
11023         this.sortType = st[this.sortType];
11024     }
11025     
11026     // set default sortType for strings and dates
11027     if(!this.sortType){
11028         switch(this.type){
11029             case "string":
11030                 this.sortType = st.asUCString;
11031                 break;
11032             case "date":
11033                 this.sortType = st.asDate;
11034                 break;
11035             default:
11036                 this.sortType = st.none;
11037         }
11038     }
11039
11040     // define once
11041     var stripRe = /[\$,%]/g;
11042
11043     // prebuilt conversion function for this field, instead of
11044     // switching every time we're reading a value
11045     if(!this.convert){
11046         var cv, dateFormat = this.dateFormat;
11047         switch(this.type){
11048             case "":
11049             case "auto":
11050             case undefined:
11051                 cv = function(v){ return v; };
11052                 break;
11053             case "string":
11054                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11055                 break;
11056             case "int":
11057                 cv = function(v){
11058                     return v !== undefined && v !== null && v !== '' ?
11059                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11060                     };
11061                 break;
11062             case "float":
11063                 cv = function(v){
11064                     return v !== undefined && v !== null && v !== '' ?
11065                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11066                     };
11067                 break;
11068             case "bool":
11069             case "boolean":
11070                 cv = function(v){ return v === true || v === "true" || v == 1; };
11071                 break;
11072             case "date":
11073                 cv = function(v){
11074                     if(!v){
11075                         return '';
11076                     }
11077                     if(v instanceof Date){
11078                         return v;
11079                     }
11080                     if(dateFormat){
11081                         if(dateFormat == "timestamp"){
11082                             return new Date(v*1000);
11083                         }
11084                         return Date.parseDate(v, dateFormat);
11085                     }
11086                     var parsed = Date.parse(v);
11087                     return parsed ? new Date(parsed) : null;
11088                 };
11089              break;
11090             
11091         }
11092         this.convert = cv;
11093     }
11094 };
11095
11096 Roo.data.Field.prototype = {
11097     dateFormat: null,
11098     defaultValue: "",
11099     mapping: null,
11100     sortType : null,
11101     sortDir : "ASC"
11102 };/*
11103  * Based on:
11104  * Ext JS Library 1.1.1
11105  * Copyright(c) 2006-2007, Ext JS, LLC.
11106  *
11107  * Originally Released Under LGPL - original licence link has changed is not relivant.
11108  *
11109  * Fork - LGPL
11110  * <script type="text/javascript">
11111  */
11112  
11113 // Base class for reading structured data from a data source.  This class is intended to be
11114 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11115
11116 /**
11117  * @class Roo.data.DataReader
11118  * Base class for reading structured data from a data source.  This class is intended to be
11119  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11120  */
11121
11122 Roo.data.DataReader = function(meta, recordType){
11123     
11124     this.meta = meta;
11125     
11126     this.recordType = recordType instanceof Array ? 
11127         Roo.data.Record.create(recordType) : recordType;
11128 };
11129
11130 Roo.data.DataReader.prototype = {
11131      /**
11132      * Create an empty record
11133      * @param {Object} data (optional) - overlay some values
11134      * @return {Roo.data.Record} record created.
11135      */
11136     newRow :  function(d) {
11137         var da =  {};
11138         this.recordType.prototype.fields.each(function(c) {
11139             switch( c.type) {
11140                 case 'int' : da[c.name] = 0; break;
11141                 case 'date' : da[c.name] = new Date(); break;
11142                 case 'float' : da[c.name] = 0.0; break;
11143                 case 'boolean' : da[c.name] = false; break;
11144                 default : da[c.name] = ""; break;
11145             }
11146             
11147         });
11148         return new this.recordType(Roo.apply(da, d));
11149     }
11150     
11151 };/*
11152  * Based on:
11153  * Ext JS Library 1.1.1
11154  * Copyright(c) 2006-2007, Ext JS, LLC.
11155  *
11156  * Originally Released Under LGPL - original licence link has changed is not relivant.
11157  *
11158  * Fork - LGPL
11159  * <script type="text/javascript">
11160  */
11161
11162 /**
11163  * @class Roo.data.DataProxy
11164  * @extends Roo.data.Observable
11165  * This class is an abstract base class for implementations which provide retrieval of
11166  * unformatted data objects.<br>
11167  * <p>
11168  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11169  * (of the appropriate type which knows how to parse the data object) to provide a block of
11170  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11171  * <p>
11172  * Custom implementations must implement the load method as described in
11173  * {@link Roo.data.HttpProxy#load}.
11174  */
11175 Roo.data.DataProxy = function(){
11176     this.addEvents({
11177         /**
11178          * @event beforeload
11179          * Fires before a network request is made to retrieve a data object.
11180          * @param {Object} This DataProxy object.
11181          * @param {Object} params The params parameter to the load function.
11182          */
11183         beforeload : true,
11184         /**
11185          * @event load
11186          * Fires before the load method's callback is called.
11187          * @param {Object} This DataProxy object.
11188          * @param {Object} o The data object.
11189          * @param {Object} arg The callback argument object passed to the load function.
11190          */
11191         load : true,
11192         /**
11193          * @event loadexception
11194          * Fires if an Exception occurs during data retrieval.
11195          * @param {Object} This DataProxy object.
11196          * @param {Object} o The data object.
11197          * @param {Object} arg The callback argument object passed to the load function.
11198          * @param {Object} e The Exception.
11199          */
11200         loadexception : true
11201     });
11202     Roo.data.DataProxy.superclass.constructor.call(this);
11203 };
11204
11205 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11206
11207     /**
11208      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11209      */
11210 /*
11211  * Based on:
11212  * Ext JS Library 1.1.1
11213  * Copyright(c) 2006-2007, Ext JS, LLC.
11214  *
11215  * Originally Released Under LGPL - original licence link has changed is not relivant.
11216  *
11217  * Fork - LGPL
11218  * <script type="text/javascript">
11219  */
11220 /**
11221  * @class Roo.data.MemoryProxy
11222  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11223  * to the Reader when its load method is called.
11224  * @constructor
11225  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11226  */
11227 Roo.data.MemoryProxy = function(data){
11228     if (data.data) {
11229         data = data.data;
11230     }
11231     Roo.data.MemoryProxy.superclass.constructor.call(this);
11232     this.data = data;
11233 };
11234
11235 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11236     
11237     /**
11238      * Load data from the requested source (in this case an in-memory
11239      * data object passed to the constructor), read the data object into
11240      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11241      * process that block using the passed callback.
11242      * @param {Object} params This parameter is not used by the MemoryProxy class.
11243      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11244      * object into a block of Roo.data.Records.
11245      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11246      * The function must be passed <ul>
11247      * <li>The Record block object</li>
11248      * <li>The "arg" argument from the load function</li>
11249      * <li>A boolean success indicator</li>
11250      * </ul>
11251      * @param {Object} scope The scope in which to call the callback
11252      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11253      */
11254     load : function(params, reader, callback, scope, arg){
11255         params = params || {};
11256         var result;
11257         try {
11258             result = reader.readRecords(this.data);
11259         }catch(e){
11260             this.fireEvent("loadexception", this, arg, null, e);
11261             callback.call(scope, null, arg, false);
11262             return;
11263         }
11264         callback.call(scope, result, arg, true);
11265     },
11266     
11267     // private
11268     update : function(params, records){
11269         
11270     }
11271 });/*
11272  * Based on:
11273  * Ext JS Library 1.1.1
11274  * Copyright(c) 2006-2007, Ext JS, LLC.
11275  *
11276  * Originally Released Under LGPL - original licence link has changed is not relivant.
11277  *
11278  * Fork - LGPL
11279  * <script type="text/javascript">
11280  */
11281 /**
11282  * @class Roo.data.HttpProxy
11283  * @extends Roo.data.DataProxy
11284  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11285  * configured to reference a certain URL.<br><br>
11286  * <p>
11287  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11288  * from which the running page was served.<br><br>
11289  * <p>
11290  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11291  * <p>
11292  * Be aware that to enable the browser to parse an XML document, the server must set
11293  * the Content-Type header in the HTTP response to "text/xml".
11294  * @constructor
11295  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11296  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11297  * will be used to make the request.
11298  */
11299 Roo.data.HttpProxy = function(conn){
11300     Roo.data.HttpProxy.superclass.constructor.call(this);
11301     // is conn a conn config or a real conn?
11302     this.conn = conn;
11303     this.useAjax = !conn || !conn.events;
11304   
11305 };
11306
11307 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11308     // thse are take from connection...
11309     
11310     /**
11311      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11312      */
11313     /**
11314      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11315      * extra parameters to each request made by this object. (defaults to undefined)
11316      */
11317     /**
11318      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11319      *  to each request made by this object. (defaults to undefined)
11320      */
11321     /**
11322      * @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)
11323      */
11324     /**
11325      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11326      */
11327      /**
11328      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11329      * @type Boolean
11330      */
11331   
11332
11333     /**
11334      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11335      * @type Boolean
11336      */
11337     /**
11338      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11339      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11340      * a finer-grained basis than the DataProxy events.
11341      */
11342     getConnection : function(){
11343         return this.useAjax ? Roo.Ajax : this.conn;
11344     },
11345
11346     /**
11347      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11348      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11349      * process that block using the passed callback.
11350      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11351      * for the request to the remote server.
11352      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11353      * object into a block of Roo.data.Records.
11354      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11355      * The function must be passed <ul>
11356      * <li>The Record block object</li>
11357      * <li>The "arg" argument from the load function</li>
11358      * <li>A boolean success indicator</li>
11359      * </ul>
11360      * @param {Object} scope The scope in which to call the callback
11361      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11362      */
11363     load : function(params, reader, callback, scope, arg){
11364         if(this.fireEvent("beforeload", this, params) !== false){
11365             var  o = {
11366                 params : params || {},
11367                 request: {
11368                     callback : callback,
11369                     scope : scope,
11370                     arg : arg
11371                 },
11372                 reader: reader,
11373                 callback : this.loadResponse,
11374                 scope: this
11375             };
11376             if(this.useAjax){
11377                 Roo.applyIf(o, this.conn);
11378                 if(this.activeRequest){
11379                     Roo.Ajax.abort(this.activeRequest);
11380                 }
11381                 this.activeRequest = Roo.Ajax.request(o);
11382             }else{
11383                 this.conn.request(o);
11384             }
11385         }else{
11386             callback.call(scope||this, null, arg, false);
11387         }
11388     },
11389
11390     // private
11391     loadResponse : function(o, success, response){
11392         delete this.activeRequest;
11393         if(!success){
11394             this.fireEvent("loadexception", this, o, response);
11395             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11396             return;
11397         }
11398         var result;
11399         try {
11400             result = o.reader.read(response);
11401         }catch(e){
11402             this.fireEvent("loadexception", this, o, response, e);
11403             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11404             return;
11405         }
11406         
11407         this.fireEvent("load", this, o, o.request.arg);
11408         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11409     },
11410
11411     // private
11412     update : function(dataSet){
11413
11414     },
11415
11416     // private
11417     updateResponse : function(dataSet){
11418
11419     }
11420 });/*
11421  * Based on:
11422  * Ext JS Library 1.1.1
11423  * Copyright(c) 2006-2007, Ext JS, LLC.
11424  *
11425  * Originally Released Under LGPL - original licence link has changed is not relivant.
11426  *
11427  * Fork - LGPL
11428  * <script type="text/javascript">
11429  */
11430
11431 /**
11432  * @class Roo.data.ScriptTagProxy
11433  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11434  * other than the originating domain of the running page.<br><br>
11435  * <p>
11436  * <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
11437  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11438  * <p>
11439  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11440  * source code that is used as the source inside a &lt;script> tag.<br><br>
11441  * <p>
11442  * In order for the browser to process the returned data, the server must wrap the data object
11443  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11444  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11445  * depending on whether the callback name was passed:
11446  * <p>
11447  * <pre><code>
11448 boolean scriptTag = false;
11449 String cb = request.getParameter("callback");
11450 if (cb != null) {
11451     scriptTag = true;
11452     response.setContentType("text/javascript");
11453 } else {
11454     response.setContentType("application/x-json");
11455 }
11456 Writer out = response.getWriter();
11457 if (scriptTag) {
11458     out.write(cb + "(");
11459 }
11460 out.print(dataBlock.toJsonString());
11461 if (scriptTag) {
11462     out.write(");");
11463 }
11464 </pre></code>
11465  *
11466  * @constructor
11467  * @param {Object} config A configuration object.
11468  */
11469 Roo.data.ScriptTagProxy = function(config){
11470     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11471     Roo.apply(this, config);
11472     this.head = document.getElementsByTagName("head")[0];
11473 };
11474
11475 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11476
11477 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11478     /**
11479      * @cfg {String} url The URL from which to request the data object.
11480      */
11481     /**
11482      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11483      */
11484     timeout : 30000,
11485     /**
11486      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11487      * the server the name of the callback function set up by the load call to process the returned data object.
11488      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11489      * javascript output which calls this named function passing the data object as its only parameter.
11490      */
11491     callbackParam : "callback",
11492     /**
11493      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11494      * name to the request.
11495      */
11496     nocache : true,
11497
11498     /**
11499      * Load data from the configured URL, read the data object into
11500      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11501      * process that block using the passed callback.
11502      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11503      * for the request to the remote server.
11504      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11505      * object into a block of Roo.data.Records.
11506      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11507      * The function must be passed <ul>
11508      * <li>The Record block object</li>
11509      * <li>The "arg" argument from the load function</li>
11510      * <li>A boolean success indicator</li>
11511      * </ul>
11512      * @param {Object} scope The scope in which to call the callback
11513      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11514      */
11515     load : function(params, reader, callback, scope, arg){
11516         if(this.fireEvent("beforeload", this, params) !== false){
11517
11518             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11519
11520             var url = this.url;
11521             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11522             if(this.nocache){
11523                 url += "&_dc=" + (new Date().getTime());
11524             }
11525             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11526             var trans = {
11527                 id : transId,
11528                 cb : "stcCallback"+transId,
11529                 scriptId : "stcScript"+transId,
11530                 params : params,
11531                 arg : arg,
11532                 url : url,
11533                 callback : callback,
11534                 scope : scope,
11535                 reader : reader
11536             };
11537             var conn = this;
11538
11539             window[trans.cb] = function(o){
11540                 conn.handleResponse(o, trans);
11541             };
11542
11543             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11544
11545             if(this.autoAbort !== false){
11546                 this.abort();
11547             }
11548
11549             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11550
11551             var script = document.createElement("script");
11552             script.setAttribute("src", url);
11553             script.setAttribute("type", "text/javascript");
11554             script.setAttribute("id", trans.scriptId);
11555             this.head.appendChild(script);
11556
11557             this.trans = trans;
11558         }else{
11559             callback.call(scope||this, null, arg, false);
11560         }
11561     },
11562
11563     // private
11564     isLoading : function(){
11565         return this.trans ? true : false;
11566     },
11567
11568     /**
11569      * Abort the current server request.
11570      */
11571     abort : function(){
11572         if(this.isLoading()){
11573             this.destroyTrans(this.trans);
11574         }
11575     },
11576
11577     // private
11578     destroyTrans : function(trans, isLoaded){
11579         this.head.removeChild(document.getElementById(trans.scriptId));
11580         clearTimeout(trans.timeoutId);
11581         if(isLoaded){
11582             window[trans.cb] = undefined;
11583             try{
11584                 delete window[trans.cb];
11585             }catch(e){}
11586         }else{
11587             // if hasn't been loaded, wait for load to remove it to prevent script error
11588             window[trans.cb] = function(){
11589                 window[trans.cb] = undefined;
11590                 try{
11591                     delete window[trans.cb];
11592                 }catch(e){}
11593             };
11594         }
11595     },
11596
11597     // private
11598     handleResponse : function(o, trans){
11599         this.trans = false;
11600         this.destroyTrans(trans, true);
11601         var result;
11602         try {
11603             result = trans.reader.readRecords(o);
11604         }catch(e){
11605             this.fireEvent("loadexception", this, o, trans.arg, e);
11606             trans.callback.call(trans.scope||window, null, trans.arg, false);
11607             return;
11608         }
11609         this.fireEvent("load", this, o, trans.arg);
11610         trans.callback.call(trans.scope||window, result, trans.arg, true);
11611     },
11612
11613     // private
11614     handleFailure : function(trans){
11615         this.trans = false;
11616         this.destroyTrans(trans, false);
11617         this.fireEvent("loadexception", this, null, trans.arg);
11618         trans.callback.call(trans.scope||window, null, trans.arg, false);
11619     }
11620 });/*
11621  * Based on:
11622  * Ext JS Library 1.1.1
11623  * Copyright(c) 2006-2007, Ext JS, LLC.
11624  *
11625  * Originally Released Under LGPL - original licence link has changed is not relivant.
11626  *
11627  * Fork - LGPL
11628  * <script type="text/javascript">
11629  */
11630
11631 /**
11632  * @class Roo.data.JsonReader
11633  * @extends Roo.data.DataReader
11634  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11635  * based on mappings in a provided Roo.data.Record constructor.
11636  * 
11637  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11638  * in the reply previously. 
11639  * 
11640  * <p>
11641  * Example code:
11642  * <pre><code>
11643 var RecordDef = Roo.data.Record.create([
11644     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11645     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11646 ]);
11647 var myReader = new Roo.data.JsonReader({
11648     totalProperty: "results",    // The property which contains the total dataset size (optional)
11649     root: "rows",                // The property which contains an Array of row objects
11650     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11651 }, RecordDef);
11652 </code></pre>
11653  * <p>
11654  * This would consume a JSON file like this:
11655  * <pre><code>
11656 { 'results': 2, 'rows': [
11657     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11658     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11659 }
11660 </code></pre>
11661  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11662  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11663  * paged from the remote server.
11664  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11665  * @cfg {String} root name of the property which contains the Array of row objects.
11666  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11667  * @cfg {Array} fields Array of field definition objects
11668  * @constructor
11669  * Create a new JsonReader
11670  * @param {Object} meta Metadata configuration options
11671  * @param {Object} recordType Either an Array of field definition objects,
11672  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11673  */
11674 Roo.data.JsonReader = function(meta, recordType){
11675     
11676     meta = meta || {};
11677     // set some defaults:
11678     Roo.applyIf(meta, {
11679         totalProperty: 'total',
11680         successProperty : 'success',
11681         root : 'data',
11682         id : 'id'
11683     });
11684     
11685     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11686 };
11687 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11688     
11689     /**
11690      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11691      * Used by Store query builder to append _requestMeta to params.
11692      * 
11693      */
11694     metaFromRemote : false,
11695     /**
11696      * This method is only used by a DataProxy which has retrieved data from a remote server.
11697      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11698      * @return {Object} data A data block which is used by an Roo.data.Store object as
11699      * a cache of Roo.data.Records.
11700      */
11701     read : function(response){
11702         var json = response.responseText;
11703        
11704         var o = /* eval:var:o */ eval("("+json+")");
11705         if(!o) {
11706             throw {message: "JsonReader.read: Json object not found"};
11707         }
11708         
11709         if(o.metaData){
11710             
11711             delete this.ef;
11712             this.metaFromRemote = true;
11713             this.meta = o.metaData;
11714             this.recordType = Roo.data.Record.create(o.metaData.fields);
11715             this.onMetaChange(this.meta, this.recordType, o);
11716         }
11717         return this.readRecords(o);
11718     },
11719
11720     // private function a store will implement
11721     onMetaChange : function(meta, recordType, o){
11722
11723     },
11724
11725     /**
11726          * @ignore
11727          */
11728     simpleAccess: function(obj, subsc) {
11729         return obj[subsc];
11730     },
11731
11732         /**
11733          * @ignore
11734          */
11735     getJsonAccessor: function(){
11736         var re = /[\[\.]/;
11737         return function(expr) {
11738             try {
11739                 return(re.test(expr))
11740                     ? new Function("obj", "return obj." + expr)
11741                     : function(obj){
11742                         return obj[expr];
11743                     };
11744             } catch(e){}
11745             return Roo.emptyFn;
11746         };
11747     }(),
11748
11749     /**
11750      * Create a data block containing Roo.data.Records from an XML document.
11751      * @param {Object} o An object which contains an Array of row objects in the property specified
11752      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11753      * which contains the total size of the dataset.
11754      * @return {Object} data A data block which is used by an Roo.data.Store object as
11755      * a cache of Roo.data.Records.
11756      */
11757     readRecords : function(o){
11758         /**
11759          * After any data loads, the raw JSON data is available for further custom processing.
11760          * @type Object
11761          */
11762         this.o = o;
11763         var s = this.meta, Record = this.recordType,
11764             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11765
11766 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11767         if (!this.ef) {
11768             if(s.totalProperty) {
11769                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11770                 }
11771                 if(s.successProperty) {
11772                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11773                 }
11774                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11775                 if (s.id) {
11776                         var g = this.getJsonAccessor(s.id);
11777                         this.getId = function(rec) {
11778                                 var r = g(rec);  
11779                                 return (r === undefined || r === "") ? null : r;
11780                         };
11781                 } else {
11782                         this.getId = function(){return null;};
11783                 }
11784             this.ef = [];
11785             for(var jj = 0; jj < fl; jj++){
11786                 f = fi[jj];
11787                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11788                 this.ef[jj] = this.getJsonAccessor(map);
11789             }
11790         }
11791
11792         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11793         if(s.totalProperty){
11794             var vt = parseInt(this.getTotal(o), 10);
11795             if(!isNaN(vt)){
11796                 totalRecords = vt;
11797             }
11798         }
11799         if(s.successProperty){
11800             var vs = this.getSuccess(o);
11801             if(vs === false || vs === 'false'){
11802                 success = false;
11803             }
11804         }
11805         var records = [];
11806         for(var i = 0; i < c; i++){
11807                 var n = root[i];
11808             var values = {};
11809             var id = this.getId(n);
11810             for(var j = 0; j < fl; j++){
11811                 f = fi[j];
11812             var v = this.ef[j](n);
11813             if (!f.convert) {
11814                 Roo.log('missing convert for ' + f.name);
11815                 Roo.log(f);
11816                 continue;
11817             }
11818             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11819             }
11820             var record = new Record(values, id);
11821             record.json = n;
11822             records[i] = record;
11823         }
11824         return {
11825             raw : o,
11826             success : success,
11827             records : records,
11828             totalRecords : totalRecords
11829         };
11830     }
11831 });/*
11832  * Based on:
11833  * Ext JS Library 1.1.1
11834  * Copyright(c) 2006-2007, Ext JS, LLC.
11835  *
11836  * Originally Released Under LGPL - original licence link has changed is not relivant.
11837  *
11838  * Fork - LGPL
11839  * <script type="text/javascript">
11840  */
11841
11842 /**
11843  * @class Roo.data.ArrayReader
11844  * @extends Roo.data.DataReader
11845  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11846  * Each element of that Array represents a row of data fields. The
11847  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11848  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11849  * <p>
11850  * Example code:.
11851  * <pre><code>
11852 var RecordDef = Roo.data.Record.create([
11853     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11854     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11855 ]);
11856 var myReader = new Roo.data.ArrayReader({
11857     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11858 }, RecordDef);
11859 </code></pre>
11860  * <p>
11861  * This would consume an Array like this:
11862  * <pre><code>
11863 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11864   </code></pre>
11865  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11866  * @constructor
11867  * Create a new JsonReader
11868  * @param {Object} meta Metadata configuration options.
11869  * @param {Object} recordType Either an Array of field definition objects
11870  * as specified to {@link Roo.data.Record#create},
11871  * or an {@link Roo.data.Record} object
11872  * created using {@link Roo.data.Record#create}.
11873  */
11874 Roo.data.ArrayReader = function(meta, recordType){
11875     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11876 };
11877
11878 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11879     /**
11880      * Create a data block containing Roo.data.Records from an XML document.
11881      * @param {Object} o An Array of row objects which represents the dataset.
11882      * @return {Object} data A data block which is used by an Roo.data.Store object as
11883      * a cache of Roo.data.Records.
11884      */
11885     readRecords : function(o){
11886         var sid = this.meta ? this.meta.id : null;
11887         var recordType = this.recordType, fields = recordType.prototype.fields;
11888         var records = [];
11889         var root = o;
11890             for(var i = 0; i < root.length; i++){
11891                     var n = root[i];
11892                 var values = {};
11893                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11894                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11895                 var f = fields.items[j];
11896                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11897                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11898                 v = f.convert(v);
11899                 values[f.name] = v;
11900             }
11901                 var record = new recordType(values, id);
11902                 record.json = n;
11903                 records[records.length] = record;
11904             }
11905             return {
11906                 records : records,
11907                 totalRecords : records.length
11908             };
11909     }
11910 });/*
11911  * - LGPL
11912  * * 
11913  */
11914
11915 /**
11916  * @class Roo.bootstrap.ComboBox
11917  * @extends Roo.bootstrap.TriggerField
11918  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11919  * @cfg {Boolean} append (true|false) default false
11920  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11921  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11922  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11923  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11924  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11925  * @cfg {Boolean} animate default true
11926  * @cfg {Boolean} emptyResultText only for touch device
11927  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11928  * @constructor
11929  * Create a new ComboBox.
11930  * @param {Object} config Configuration options
11931  */
11932 Roo.bootstrap.ComboBox = function(config){
11933     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11934     this.addEvents({
11935         /**
11936          * @event expand
11937          * Fires when the dropdown list is expanded
11938              * @param {Roo.bootstrap.ComboBox} combo This combo box
11939              */
11940         'expand' : true,
11941         /**
11942          * @event collapse
11943          * Fires when the dropdown list is collapsed
11944              * @param {Roo.bootstrap.ComboBox} combo This combo box
11945              */
11946         'collapse' : true,
11947         /**
11948          * @event beforeselect
11949          * Fires before a list item is selected. Return false to cancel the selection.
11950              * @param {Roo.bootstrap.ComboBox} combo This combo box
11951              * @param {Roo.data.Record} record The data record returned from the underlying store
11952              * @param {Number} index The index of the selected item in the dropdown list
11953              */
11954         'beforeselect' : true,
11955         /**
11956          * @event select
11957          * Fires when a list item is selected
11958              * @param {Roo.bootstrap.ComboBox} combo This combo box
11959              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11960              * @param {Number} index The index of the selected item in the dropdown list
11961              */
11962         'select' : true,
11963         /**
11964          * @event beforequery
11965          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11966          * The event object passed has these properties:
11967              * @param {Roo.bootstrap.ComboBox} combo This combo box
11968              * @param {String} query The query
11969              * @param {Boolean} forceAll true to force "all" query
11970              * @param {Boolean} cancel true to cancel the query
11971              * @param {Object} e The query event object
11972              */
11973         'beforequery': true,
11974          /**
11975          * @event add
11976          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11977              * @param {Roo.bootstrap.ComboBox} combo This combo box
11978              */
11979         'add' : true,
11980         /**
11981          * @event edit
11982          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11983              * @param {Roo.bootstrap.ComboBox} combo This combo box
11984              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11985              */
11986         'edit' : true,
11987         /**
11988          * @event remove
11989          * Fires when the remove value from the combobox array
11990              * @param {Roo.bootstrap.ComboBox} combo This combo box
11991              */
11992         'remove' : true,
11993         /**
11994          * @event afterremove
11995          * Fires when the remove value from the combobox array
11996              * @param {Roo.bootstrap.ComboBox} combo This combo box
11997              */
11998         'afterremove' : true,
11999         /**
12000          * @event specialfilter
12001          * Fires when specialfilter
12002             * @param {Roo.bootstrap.ComboBox} combo This combo box
12003             */
12004         'specialfilter' : true,
12005         /**
12006          * @event tick
12007          * Fires when tick the element
12008             * @param {Roo.bootstrap.ComboBox} combo This combo box
12009             */
12010         'tick' : true,
12011         /**
12012          * @event touchviewdisplay
12013          * Fires when touch view require special display (default is using displayField)
12014             * @param {Roo.bootstrap.ComboBox} combo This combo box
12015             * @param {Object} cfg set html .
12016             */
12017         'touchviewdisplay' : true
12018         
12019     });
12020     
12021     this.item = [];
12022     this.tickItems = [];
12023     
12024     this.selectedIndex = -1;
12025     if(this.mode == 'local'){
12026         if(config.queryDelay === undefined){
12027             this.queryDelay = 10;
12028         }
12029         if(config.minChars === undefined){
12030             this.minChars = 0;
12031         }
12032     }
12033 };
12034
12035 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12036      
12037     /**
12038      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12039      * rendering into an Roo.Editor, defaults to false)
12040      */
12041     /**
12042      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12043      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12044      */
12045     /**
12046      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12047      */
12048     /**
12049      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12050      * the dropdown list (defaults to undefined, with no header element)
12051      */
12052
12053      /**
12054      * @cfg {String/Roo.Template} tpl The template to use to render the output
12055      */
12056      
12057      /**
12058      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12059      */
12060     listWidth: undefined,
12061     /**
12062      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12063      * mode = 'remote' or 'text' if mode = 'local')
12064      */
12065     displayField: undefined,
12066     
12067     /**
12068      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12069      * mode = 'remote' or 'value' if mode = 'local'). 
12070      * Note: use of a valueField requires the user make a selection
12071      * in order for a value to be mapped.
12072      */
12073     valueField: undefined,
12074     /**
12075      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12076      */
12077     modalTitle : '',
12078     
12079     /**
12080      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12081      * field's data value (defaults to the underlying DOM element's name)
12082      */
12083     hiddenName: undefined,
12084     /**
12085      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12086      */
12087     listClass: '',
12088     /**
12089      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12090      */
12091     selectedClass: 'active',
12092     
12093     /**
12094      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12095      */
12096     shadow:'sides',
12097     /**
12098      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12099      * anchor positions (defaults to 'tl-bl')
12100      */
12101     listAlign: 'tl-bl?',
12102     /**
12103      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12104      */
12105     maxHeight: 300,
12106     /**
12107      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12108      * query specified by the allQuery config option (defaults to 'query')
12109      */
12110     triggerAction: 'query',
12111     /**
12112      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12113      * (defaults to 4, does not apply if editable = false)
12114      */
12115     minChars : 4,
12116     /**
12117      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12118      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12119      */
12120     typeAhead: false,
12121     /**
12122      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12123      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12124      */
12125     queryDelay: 500,
12126     /**
12127      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12128      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12129      */
12130     pageSize: 0,
12131     /**
12132      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12133      * when editable = true (defaults to false)
12134      */
12135     selectOnFocus:false,
12136     /**
12137      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12138      */
12139     queryParam: 'query',
12140     /**
12141      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12142      * when mode = 'remote' (defaults to 'Loading...')
12143      */
12144     loadingText: 'Loading...',
12145     /**
12146      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12147      */
12148     resizable: false,
12149     /**
12150      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12151      */
12152     handleHeight : 8,
12153     /**
12154      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12155      * traditional select (defaults to true)
12156      */
12157     editable: true,
12158     /**
12159      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12160      */
12161     allQuery: '',
12162     /**
12163      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12164      */
12165     mode: 'remote',
12166     /**
12167      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12168      * listWidth has a higher value)
12169      */
12170     minListWidth : 70,
12171     /**
12172      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12173      * allow the user to set arbitrary text into the field (defaults to false)
12174      */
12175     forceSelection:false,
12176     /**
12177      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12178      * if typeAhead = true (defaults to 250)
12179      */
12180     typeAheadDelay : 250,
12181     /**
12182      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12183      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12184      */
12185     valueNotFoundText : undefined,
12186     /**
12187      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12188      */
12189     blockFocus : false,
12190     
12191     /**
12192      * @cfg {Boolean} disableClear Disable showing of clear button.
12193      */
12194     disableClear : false,
12195     /**
12196      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12197      */
12198     alwaysQuery : false,
12199     
12200     /**
12201      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12202      */
12203     multiple : false,
12204     
12205     /**
12206      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12207      */
12208     invalidClass : "has-warning",
12209     
12210     /**
12211      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12212      */
12213     validClass : "has-success",
12214     
12215     /**
12216      * @cfg {Boolean} specialFilter (true|false) special filter default false
12217      */
12218     specialFilter : false,
12219     
12220     /**
12221      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12222      */
12223     mobileTouchView : true,
12224     
12225     //private
12226     addicon : false,
12227     editicon: false,
12228     
12229     page: 0,
12230     hasQuery: false,
12231     append: false,
12232     loadNext: false,
12233     autoFocus : true,
12234     tickable : false,
12235     btnPosition : 'right',
12236     triggerList : true,
12237     showToggleBtn : true,
12238     animate : true,
12239     emptyResultText: 'Empty',
12240     triggerText : 'Select',
12241     
12242     // element that contains real text value.. (when hidden is used..)
12243     
12244     getAutoCreate : function()
12245     {
12246         var cfg = false;
12247         
12248         /*
12249          * Touch Devices
12250          */
12251         
12252         if(Roo.isTouch && this.mobileTouchView){
12253             cfg = this.getAutoCreateTouchView();
12254             return cfg;;
12255         }
12256         
12257         /*
12258          *  Normal ComboBox
12259          */
12260         if(!this.tickable){
12261             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12262             return cfg;
12263         }
12264         
12265         /*
12266          *  ComboBox with tickable selections
12267          */
12268              
12269         var align = this.labelAlign || this.parentLabelAlign();
12270         
12271         cfg = {
12272             cls : 'form-group roo-combobox-tickable' //input-group
12273         };
12274         
12275         var buttons = {
12276             tag : 'div',
12277             cls : 'tickable-buttons',
12278             cn : [
12279                 {
12280                     tag : 'button',
12281                     type : 'button',
12282                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12283                     html : this.triggerText
12284                 },
12285                 {
12286                     tag : 'button',
12287                     type : 'button',
12288                     name : 'ok',
12289                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12290                     html : 'Done'
12291                 },
12292                 {
12293                     tag : 'button',
12294                     type : 'button',
12295                     name : 'cancel',
12296                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12297                     html : 'Cancel'
12298                 }
12299             ]
12300         };
12301         
12302         if(this.editable){
12303             buttons.cn.unshift({
12304                 tag: 'input',
12305                 cls: 'roo-select2-search-field-input'
12306             });
12307         }
12308         
12309         var _this = this;
12310         
12311         Roo.each(buttons.cn, function(c){
12312             if (_this.size) {
12313                 c.cls += ' btn-' + _this.size;
12314             }
12315
12316             if (_this.disabled) {
12317                 c.disabled = true;
12318             }
12319         });
12320         
12321         var box = {
12322             tag: 'div',
12323             cn: [
12324                 {
12325                     tag: 'input',
12326                     type : 'hidden',
12327                     cls: 'form-hidden-field'
12328                 },
12329                 {
12330                     tag: 'ul',
12331                     cls: 'roo-select2-choices',
12332                     cn:[
12333                         {
12334                             tag: 'li',
12335                             cls: 'roo-select2-search-field',
12336                             cn: [
12337
12338                                 buttons
12339                             ]
12340                         }
12341                     ]
12342                 }
12343             ]
12344         };
12345         
12346         var combobox = {
12347             cls: 'roo-select2-container input-group roo-select2-container-multi',
12348             cn: [
12349                 box
12350 //                {
12351 //                    tag: 'ul',
12352 //                    cls: 'typeahead typeahead-long dropdown-menu',
12353 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12354 //                }
12355             ]
12356         };
12357         
12358         if(this.hasFeedback && !this.allowBlank){
12359             
12360             var feedback = {
12361                 tag: 'span',
12362                 cls: 'glyphicon form-control-feedback'
12363             };
12364
12365             combobox.cn.push(feedback);
12366         }
12367         
12368         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12369             
12370 //                Roo.log("left and has label");
12371             cfg.cn = [
12372                 {
12373                     tag : 'i',
12374                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12375                     tooltip : 'This field is required'
12376                 },
12377                 {
12378                     tag: 'label',
12379                     'for' :  id,
12380                     cls : 'control-label col-sm-' + this.labelWidth,
12381                     html : this.fieldLabel
12382
12383                 },
12384                 {
12385                     cls : "col-sm-" + (12 - this.labelWidth), 
12386                     cn: [
12387                         combobox
12388                     ]
12389                 }
12390
12391             ];
12392
12393             if(this.indicatorpos == 'right'){
12394                 
12395                 cfg.cn = [
12396                     {
12397                         tag: 'label',
12398                         'for' :  id,
12399                         cls : 'control-label col-sm-' + this.labelWidth,
12400                         html : this.fieldLabel
12401
12402                     },
12403                     {
12404                         tag : 'i',
12405                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12406                         tooltip : 'This field is required'
12407                     },
12408                     {
12409                         cls : "col-sm-" + (12 - this.labelWidth), 
12410                         cn: [
12411                             combobox
12412                         ]
12413                     }
12414
12415                 ];
12416             
12417             }
12418                 
12419                 
12420         } else if ( this.fieldLabel.length) {
12421 //                Roo.log(" label");
12422                  cfg.cn = [
12423                     {
12424                         tag : 'i',
12425                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12426                         tooltip : 'This field is required'
12427                     },
12428                     {
12429                         tag: 'label',
12430                         //cls : 'input-group-addon',
12431                         html : this.fieldLabel
12432                         
12433                     },
12434                     
12435                     combobox
12436                     
12437                 ];
12438                 
12439                 if(this.indicatorpos == 'right'){
12440                     
12441                     cfg.cn = [
12442                         {
12443                             tag: 'label',
12444                             //cls : 'input-group-addon',
12445                             html : this.fieldLabel
12446
12447                         },
12448                         
12449                         {
12450                             tag : 'i',
12451                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12452                             tooltip : 'This field is required'
12453                         },
12454                         
12455                         combobox
12456
12457                     ];
12458                 
12459                 }
12460
12461         } else {
12462             
12463 //                Roo.log(" no label && no align");
12464                 cfg = combobox
12465                      
12466                 
12467         }
12468          
12469         var settings=this;
12470         ['xs','sm','md','lg'].map(function(size){
12471             if (settings[size]) {
12472                 cfg.cls += ' col-' + size + '-' + settings[size];
12473             }
12474         });
12475         
12476         return cfg;
12477         
12478     },
12479     
12480     _initEventsCalled : false,
12481     
12482     // private
12483     initEvents: function()
12484     {
12485         
12486         if (this._initEventsCalled) { // as we call render... prevent looping...
12487             return;
12488         }
12489         this._initEventsCalled = true;
12490         
12491         if (!this.store) {
12492             throw "can not find store for combo";
12493         }
12494         
12495         this.store = Roo.factory(this.store, Roo.data);
12496         
12497         // if we are building from html. then this element is so complex, that we can not really
12498         // use the rendered HTML.
12499         // so we have to trash and replace the previous code.
12500         if (Roo.XComponent.build_from_html) {
12501             
12502             // remove this element....
12503             var e = this.el.dom, k=0;
12504             while (e ) { e = e.previousSibling;  ++k;}
12505
12506             this.el.remove();
12507             
12508             this.el=false;
12509             this.rendered = false;
12510             
12511             this.render(this.parent().getChildContainer(true), k);
12512             
12513             
12514             
12515         }
12516         
12517         
12518         /*
12519          * Touch Devices
12520          */
12521         
12522         if(Roo.isTouch && this.mobileTouchView){
12523             this.initTouchView();
12524             return;
12525         }
12526         
12527         if(this.tickable){
12528             this.initTickableEvents();
12529             return;
12530         }
12531         
12532         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12533         
12534         if(this.hiddenName){
12535             
12536             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12537             
12538             this.hiddenField.dom.value =
12539                 this.hiddenValue !== undefined ? this.hiddenValue :
12540                 this.value !== undefined ? this.value : '';
12541
12542             // prevent input submission
12543             this.el.dom.removeAttribute('name');
12544             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12545              
12546              
12547         }
12548         //if(Roo.isGecko){
12549         //    this.el.dom.setAttribute('autocomplete', 'off');
12550         //}
12551         
12552         var cls = 'x-combo-list';
12553         
12554         //this.list = new Roo.Layer({
12555         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12556         //});
12557         
12558         var _this = this;
12559         
12560         (function(){
12561             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12562             _this.list.setWidth(lw);
12563         }).defer(100);
12564         
12565         this.list.on('mouseover', this.onViewOver, this);
12566         this.list.on('mousemove', this.onViewMove, this);
12567         
12568         this.list.on('scroll', this.onViewScroll, this);
12569         
12570         /*
12571         this.list.swallowEvent('mousewheel');
12572         this.assetHeight = 0;
12573
12574         if(this.title){
12575             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12576             this.assetHeight += this.header.getHeight();
12577         }
12578
12579         this.innerList = this.list.createChild({cls:cls+'-inner'});
12580         this.innerList.on('mouseover', this.onViewOver, this);
12581         this.innerList.on('mousemove', this.onViewMove, this);
12582         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12583         
12584         if(this.allowBlank && !this.pageSize && !this.disableClear){
12585             this.footer = this.list.createChild({cls:cls+'-ft'});
12586             this.pageTb = new Roo.Toolbar(this.footer);
12587            
12588         }
12589         if(this.pageSize){
12590             this.footer = this.list.createChild({cls:cls+'-ft'});
12591             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12592                     {pageSize: this.pageSize});
12593             
12594         }
12595         
12596         if (this.pageTb && this.allowBlank && !this.disableClear) {
12597             var _this = this;
12598             this.pageTb.add(new Roo.Toolbar.Fill(), {
12599                 cls: 'x-btn-icon x-btn-clear',
12600                 text: '&#160;',
12601                 handler: function()
12602                 {
12603                     _this.collapse();
12604                     _this.clearValue();
12605                     _this.onSelect(false, -1);
12606                 }
12607             });
12608         }
12609         if (this.footer) {
12610             this.assetHeight += this.footer.getHeight();
12611         }
12612         */
12613             
12614         if(!this.tpl){
12615             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12616         }
12617
12618         this.view = new Roo.View(this.list, this.tpl, {
12619             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12620         });
12621         //this.view.wrapEl.setDisplayed(false);
12622         this.view.on('click', this.onViewClick, this);
12623         
12624         
12625         
12626         this.store.on('beforeload', this.onBeforeLoad, this);
12627         this.store.on('load', this.onLoad, this);
12628         this.store.on('loadexception', this.onLoadException, this);
12629         /*
12630         if(this.resizable){
12631             this.resizer = new Roo.Resizable(this.list,  {
12632                pinned:true, handles:'se'
12633             });
12634             this.resizer.on('resize', function(r, w, h){
12635                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12636                 this.listWidth = w;
12637                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12638                 this.restrictHeight();
12639             }, this);
12640             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12641         }
12642         */
12643         if(!this.editable){
12644             this.editable = true;
12645             this.setEditable(false);
12646         }
12647         
12648         /*
12649         
12650         if (typeof(this.events.add.listeners) != 'undefined') {
12651             
12652             this.addicon = this.wrap.createChild(
12653                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12654        
12655             this.addicon.on('click', function(e) {
12656                 this.fireEvent('add', this);
12657             }, this);
12658         }
12659         if (typeof(this.events.edit.listeners) != 'undefined') {
12660             
12661             this.editicon = this.wrap.createChild(
12662                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12663             if (this.addicon) {
12664                 this.editicon.setStyle('margin-left', '40px');
12665             }
12666             this.editicon.on('click', function(e) {
12667                 
12668                 // we fire even  if inothing is selected..
12669                 this.fireEvent('edit', this, this.lastData );
12670                 
12671             }, this);
12672         }
12673         */
12674         
12675         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12676             "up" : function(e){
12677                 this.inKeyMode = true;
12678                 this.selectPrev();
12679             },
12680
12681             "down" : function(e){
12682                 if(!this.isExpanded()){
12683                     this.onTriggerClick();
12684                 }else{
12685                     this.inKeyMode = true;
12686                     this.selectNext();
12687                 }
12688             },
12689
12690             "enter" : function(e){
12691 //                this.onViewClick();
12692                 //return true;
12693                 this.collapse();
12694                 
12695                 if(this.fireEvent("specialkey", this, e)){
12696                     this.onViewClick(false);
12697                 }
12698                 
12699                 return true;
12700             },
12701
12702             "esc" : function(e){
12703                 this.collapse();
12704             },
12705
12706             "tab" : function(e){
12707                 this.collapse();
12708                 
12709                 if(this.fireEvent("specialkey", this, e)){
12710                     this.onViewClick(false);
12711                 }
12712                 
12713                 return true;
12714             },
12715
12716             scope : this,
12717
12718             doRelay : function(foo, bar, hname){
12719                 if(hname == 'down' || this.scope.isExpanded()){
12720                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12721                 }
12722                 return true;
12723             },
12724
12725             forceKeyDown: true
12726         });
12727         
12728         
12729         this.queryDelay = Math.max(this.queryDelay || 10,
12730                 this.mode == 'local' ? 10 : 250);
12731         
12732         
12733         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12734         
12735         if(this.typeAhead){
12736             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12737         }
12738         if(this.editable !== false){
12739             this.inputEl().on("keyup", this.onKeyUp, this);
12740         }
12741         if(this.forceSelection){
12742             this.inputEl().on('blur', this.doForce, this);
12743         }
12744         
12745         if(this.multiple){
12746             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12747             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12748         }
12749     },
12750     
12751     initTickableEvents: function()
12752     {   
12753         this.createList();
12754         
12755         if(this.hiddenName){
12756             
12757             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12758             
12759             this.hiddenField.dom.value =
12760                 this.hiddenValue !== undefined ? this.hiddenValue :
12761                 this.value !== undefined ? this.value : '';
12762
12763             // prevent input submission
12764             this.el.dom.removeAttribute('name');
12765             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12766              
12767              
12768         }
12769         
12770 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12771         
12772         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12773         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12774         if(this.triggerList){
12775             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12776         }
12777          
12778         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12779         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12780         
12781         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12782         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12783         
12784         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12785         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12786         
12787         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12788         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12789         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12790         
12791         this.okBtn.hide();
12792         this.cancelBtn.hide();
12793         
12794         var _this = this;
12795         
12796         (function(){
12797             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12798             _this.list.setWidth(lw);
12799         }).defer(100);
12800         
12801         this.list.on('mouseover', this.onViewOver, this);
12802         this.list.on('mousemove', this.onViewMove, this);
12803         
12804         this.list.on('scroll', this.onViewScroll, this);
12805         
12806         if(!this.tpl){
12807             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>';
12808         }
12809
12810         this.view = new Roo.View(this.list, this.tpl, {
12811             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12812         });
12813         
12814         //this.view.wrapEl.setDisplayed(false);
12815         this.view.on('click', this.onViewClick, this);
12816         
12817         
12818         
12819         this.store.on('beforeload', this.onBeforeLoad, this);
12820         this.store.on('load', this.onLoad, this);
12821         this.store.on('loadexception', this.onLoadException, this);
12822         
12823         if(this.editable){
12824             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12825                 "up" : function(e){
12826                     this.inKeyMode = true;
12827                     this.selectPrev();
12828                 },
12829
12830                 "down" : function(e){
12831                     this.inKeyMode = true;
12832                     this.selectNext();
12833                 },
12834
12835                 "enter" : function(e){
12836                     if(this.fireEvent("specialkey", this, e)){
12837                         this.onViewClick(false);
12838                     }
12839                     
12840                     return true;
12841                 },
12842
12843                 "esc" : function(e){
12844                     this.onTickableFooterButtonClick(e, false, false);
12845                 },
12846
12847                 "tab" : function(e){
12848                     this.fireEvent("specialkey", this, e);
12849                     
12850                     this.onTickableFooterButtonClick(e, false, false);
12851                     
12852                     return true;
12853                 },
12854
12855                 scope : this,
12856
12857                 doRelay : function(e, fn, key){
12858                     if(this.scope.isExpanded()){
12859                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12860                     }
12861                     return true;
12862                 },
12863
12864                 forceKeyDown: true
12865             });
12866         }
12867         
12868         this.queryDelay = Math.max(this.queryDelay || 10,
12869                 this.mode == 'local' ? 10 : 250);
12870         
12871         
12872         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12873         
12874         if(this.typeAhead){
12875             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12876         }
12877         
12878         if(this.editable !== false){
12879             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12880         }
12881         
12882     },
12883
12884     onDestroy : function(){
12885         if(this.view){
12886             this.view.setStore(null);
12887             this.view.el.removeAllListeners();
12888             this.view.el.remove();
12889             this.view.purgeListeners();
12890         }
12891         if(this.list){
12892             this.list.dom.innerHTML  = '';
12893         }
12894         
12895         if(this.store){
12896             this.store.un('beforeload', this.onBeforeLoad, this);
12897             this.store.un('load', this.onLoad, this);
12898             this.store.un('loadexception', this.onLoadException, this);
12899         }
12900         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12901     },
12902
12903     // private
12904     fireKey : function(e){
12905         if(e.isNavKeyPress() && !this.list.isVisible()){
12906             this.fireEvent("specialkey", this, e);
12907         }
12908     },
12909
12910     // private
12911     onResize: function(w, h){
12912 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12913 //        
12914 //        if(typeof w != 'number'){
12915 //            // we do not handle it!?!?
12916 //            return;
12917 //        }
12918 //        var tw = this.trigger.getWidth();
12919 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12920 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12921 //        var x = w - tw;
12922 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12923 //            
12924 //        //this.trigger.setStyle('left', x+'px');
12925 //        
12926 //        if(this.list && this.listWidth === undefined){
12927 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12928 //            this.list.setWidth(lw);
12929 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12930 //        }
12931         
12932     
12933         
12934     },
12935
12936     /**
12937      * Allow or prevent the user from directly editing the field text.  If false is passed,
12938      * the user will only be able to select from the items defined in the dropdown list.  This method
12939      * is the runtime equivalent of setting the 'editable' config option at config time.
12940      * @param {Boolean} value True to allow the user to directly edit the field text
12941      */
12942     setEditable : function(value){
12943         if(value == this.editable){
12944             return;
12945         }
12946         this.editable = value;
12947         if(!value){
12948             this.inputEl().dom.setAttribute('readOnly', true);
12949             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12950             this.inputEl().addClass('x-combo-noedit');
12951         }else{
12952             this.inputEl().dom.setAttribute('readOnly', false);
12953             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12954             this.inputEl().removeClass('x-combo-noedit');
12955         }
12956     },
12957
12958     // private
12959     
12960     onBeforeLoad : function(combo,opts){
12961         if(!this.hasFocus){
12962             return;
12963         }
12964          if (!opts.add) {
12965             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12966          }
12967         this.restrictHeight();
12968         this.selectedIndex = -1;
12969     },
12970
12971     // private
12972     onLoad : function(){
12973         
12974         this.hasQuery = false;
12975         
12976         if(!this.hasFocus){
12977             return;
12978         }
12979         
12980         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12981             this.loading.hide();
12982         }
12983              
12984         if(this.store.getCount() > 0){
12985             this.expand();
12986             this.restrictHeight();
12987             if(this.lastQuery == this.allQuery){
12988                 if(this.editable && !this.tickable){
12989                     this.inputEl().dom.select();
12990                 }
12991                 
12992                 if(
12993                     !this.selectByValue(this.value, true) &&
12994                     this.autoFocus && 
12995                     (
12996                         !this.store.lastOptions ||
12997                         typeof(this.store.lastOptions.add) == 'undefined' || 
12998                         this.store.lastOptions.add != true
12999                     )
13000                 ){
13001                     this.select(0, true);
13002                 }
13003             }else{
13004                 if(this.autoFocus){
13005                     this.selectNext();
13006                 }
13007                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13008                     this.taTask.delay(this.typeAheadDelay);
13009                 }
13010             }
13011         }else{
13012             this.onEmptyResults();
13013         }
13014         
13015         //this.el.focus();
13016     },
13017     // private
13018     onLoadException : function()
13019     {
13020         this.hasQuery = false;
13021         
13022         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13023             this.loading.hide();
13024         }
13025         
13026         if(this.tickable && this.editable){
13027             return;
13028         }
13029         
13030         this.collapse();
13031         // only causes errors at present
13032         //Roo.log(this.store.reader.jsonData);
13033         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13034             // fixme
13035             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13036         //}
13037         
13038         
13039     },
13040     // private
13041     onTypeAhead : function(){
13042         if(this.store.getCount() > 0){
13043             var r = this.store.getAt(0);
13044             var newValue = r.data[this.displayField];
13045             var len = newValue.length;
13046             var selStart = this.getRawValue().length;
13047             
13048             if(selStart != len){
13049                 this.setRawValue(newValue);
13050                 this.selectText(selStart, newValue.length);
13051             }
13052         }
13053     },
13054
13055     // private
13056     onSelect : function(record, index){
13057         
13058         if(this.fireEvent('beforeselect', this, record, index) !== false){
13059         
13060             this.setFromData(index > -1 ? record.data : false);
13061             
13062             this.collapse();
13063             this.fireEvent('select', this, record, index);
13064         }
13065     },
13066
13067     /**
13068      * Returns the currently selected field value or empty string if no value is set.
13069      * @return {String} value The selected value
13070      */
13071     getValue : function(){
13072         
13073         if(this.multiple){
13074             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13075         }
13076         
13077         if(this.valueField){
13078             return typeof this.value != 'undefined' ? this.value : '';
13079         }else{
13080             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13081         }
13082     },
13083
13084     /**
13085      * Clears any text/value currently set in the field
13086      */
13087     clearValue : function(){
13088         if(this.hiddenField){
13089             this.hiddenField.dom.value = '';
13090         }
13091         this.value = '';
13092         this.setRawValue('');
13093         this.lastSelectionText = '';
13094         this.lastData = false;
13095         
13096         var close = this.closeTriggerEl();
13097         
13098         if(close){
13099             close.hide();
13100         }
13101         
13102         this.validate();
13103         
13104     },
13105
13106     /**
13107      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13108      * will be displayed in the field.  If the value does not match the data value of an existing item,
13109      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13110      * Otherwise the field will be blank (although the value will still be set).
13111      * @param {String} value The value to match
13112      */
13113     setValue : function(v){
13114         if(this.multiple){
13115             this.syncValue();
13116             return;
13117         }
13118         
13119         var text = v;
13120         if(this.valueField){
13121             var r = this.findRecord(this.valueField, v);
13122             if(r){
13123                 text = r.data[this.displayField];
13124             }else if(this.valueNotFoundText !== undefined){
13125                 text = this.valueNotFoundText;
13126             }
13127         }
13128         this.lastSelectionText = text;
13129         if(this.hiddenField){
13130             this.hiddenField.dom.value = v;
13131         }
13132         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13133         this.value = v;
13134         
13135         var close = this.closeTriggerEl();
13136         
13137         if(close){
13138             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13139         }
13140         
13141         this.validate();
13142     },
13143     /**
13144      * @property {Object} the last set data for the element
13145      */
13146     
13147     lastData : false,
13148     /**
13149      * Sets the value of the field based on a object which is related to the record format for the store.
13150      * @param {Object} value the value to set as. or false on reset?
13151      */
13152     setFromData : function(o){
13153         
13154         if(this.multiple){
13155             this.addItem(o);
13156             return;
13157         }
13158             
13159         var dv = ''; // display value
13160         var vv = ''; // value value..
13161         this.lastData = o;
13162         if (this.displayField) {
13163             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13164         } else {
13165             // this is an error condition!!!
13166             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13167         }
13168         
13169         if(this.valueField){
13170             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13171         }
13172         
13173         var close = this.closeTriggerEl();
13174         
13175         if(close){
13176             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13177         }
13178         
13179         if(this.hiddenField){
13180             this.hiddenField.dom.value = vv;
13181             
13182             this.lastSelectionText = dv;
13183             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13184             this.value = vv;
13185             return;
13186         }
13187         // no hidden field.. - we store the value in 'value', but still display
13188         // display field!!!!
13189         this.lastSelectionText = dv;
13190         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13191         this.value = vv;
13192         
13193         
13194         
13195     },
13196     // private
13197     reset : function(){
13198         // overridden so that last data is reset..
13199         
13200         if(this.multiple){
13201             this.clearItem();
13202             return;
13203         }
13204         
13205         this.setValue(this.originalValue);
13206         //this.clearInvalid();
13207         this.lastData = false;
13208         if (this.view) {
13209             this.view.clearSelections();
13210         }
13211         
13212         this.validate();
13213     },
13214     // private
13215     findRecord : function(prop, value){
13216         var record;
13217         if(this.store.getCount() > 0){
13218             this.store.each(function(r){
13219                 if(r.data[prop] == value){
13220                     record = r;
13221                     return false;
13222                 }
13223                 return true;
13224             });
13225         }
13226         return record;
13227     },
13228     
13229     getName: function()
13230     {
13231         // returns hidden if it's set..
13232         if (!this.rendered) {return ''};
13233         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13234         
13235     },
13236     // private
13237     onViewMove : function(e, t){
13238         this.inKeyMode = false;
13239     },
13240
13241     // private
13242     onViewOver : function(e, t){
13243         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13244             return;
13245         }
13246         var item = this.view.findItemFromChild(t);
13247         
13248         if(item){
13249             var index = this.view.indexOf(item);
13250             this.select(index, false);
13251         }
13252     },
13253
13254     // private
13255     onViewClick : function(view, doFocus, el, e)
13256     {
13257         var index = this.view.getSelectedIndexes()[0];
13258         
13259         var r = this.store.getAt(index);
13260         
13261         if(this.tickable){
13262             
13263             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13264                 return;
13265             }
13266             
13267             var rm = false;
13268             var _this = this;
13269             
13270             Roo.each(this.tickItems, function(v,k){
13271                 
13272                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13273                     Roo.log(v);
13274                     _this.tickItems.splice(k, 1);
13275                     
13276                     if(typeof(e) == 'undefined' && view == false){
13277                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13278                     }
13279                     
13280                     rm = true;
13281                     return;
13282                 }
13283             });
13284             
13285             if(rm){
13286                 return;
13287             }
13288             
13289             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13290                 this.tickItems.push(r.data);
13291             }
13292             
13293             if(typeof(e) == 'undefined' && view == false){
13294                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13295             }
13296                     
13297             return;
13298         }
13299         
13300         if(r){
13301             this.onSelect(r, index);
13302         }
13303         if(doFocus !== false && !this.blockFocus){
13304             this.inputEl().focus();
13305         }
13306     },
13307
13308     // private
13309     restrictHeight : function(){
13310         //this.innerList.dom.style.height = '';
13311         //var inner = this.innerList.dom;
13312         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13313         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13314         //this.list.beginUpdate();
13315         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13316         this.list.alignTo(this.inputEl(), this.listAlign);
13317         this.list.alignTo(this.inputEl(), this.listAlign);
13318         //this.list.endUpdate();
13319     },
13320
13321     // private
13322     onEmptyResults : function(){
13323         
13324         if(this.tickable && this.editable){
13325             this.restrictHeight();
13326             return;
13327         }
13328         
13329         this.collapse();
13330     },
13331
13332     /**
13333      * Returns true if the dropdown list is expanded, else false.
13334      */
13335     isExpanded : function(){
13336         return this.list.isVisible();
13337     },
13338
13339     /**
13340      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13341      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13342      * @param {String} value The data value of the item to select
13343      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13344      * selected item if it is not currently in view (defaults to true)
13345      * @return {Boolean} True if the value matched an item in the list, else false
13346      */
13347     selectByValue : function(v, scrollIntoView){
13348         if(v !== undefined && v !== null){
13349             var r = this.findRecord(this.valueField || this.displayField, v);
13350             if(r){
13351                 this.select(this.store.indexOf(r), scrollIntoView);
13352                 return true;
13353             }
13354         }
13355         return false;
13356     },
13357
13358     /**
13359      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13360      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13361      * @param {Number} index The zero-based index of the list item to select
13362      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13363      * selected item if it is not currently in view (defaults to true)
13364      */
13365     select : function(index, scrollIntoView){
13366         this.selectedIndex = index;
13367         this.view.select(index);
13368         if(scrollIntoView !== false){
13369             var el = this.view.getNode(index);
13370             /*
13371              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13372              */
13373             if(el){
13374                 this.list.scrollChildIntoView(el, false);
13375             }
13376         }
13377     },
13378
13379     // private
13380     selectNext : function(){
13381         var ct = this.store.getCount();
13382         if(ct > 0){
13383             if(this.selectedIndex == -1){
13384                 this.select(0);
13385             }else if(this.selectedIndex < ct-1){
13386                 this.select(this.selectedIndex+1);
13387             }
13388         }
13389     },
13390
13391     // private
13392     selectPrev : function(){
13393         var ct = this.store.getCount();
13394         if(ct > 0){
13395             if(this.selectedIndex == -1){
13396                 this.select(0);
13397             }else if(this.selectedIndex != 0){
13398                 this.select(this.selectedIndex-1);
13399             }
13400         }
13401     },
13402
13403     // private
13404     onKeyUp : function(e){
13405         if(this.editable !== false && !e.isSpecialKey()){
13406             this.lastKey = e.getKey();
13407             this.dqTask.delay(this.queryDelay);
13408         }
13409     },
13410
13411     // private
13412     validateBlur : function(){
13413         return !this.list || !this.list.isVisible();   
13414     },
13415
13416     // private
13417     initQuery : function(){
13418         
13419         var v = this.getRawValue();
13420         
13421         if(this.tickable && this.editable){
13422             v = this.tickableInputEl().getValue();
13423         }
13424         
13425         this.doQuery(v);
13426     },
13427
13428     // private
13429     doForce : function(){
13430         if(this.inputEl().dom.value.length > 0){
13431             this.inputEl().dom.value =
13432                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13433              
13434         }
13435     },
13436
13437     /**
13438      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13439      * query allowing the query action to be canceled if needed.
13440      * @param {String} query The SQL query to execute
13441      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13442      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13443      * saved in the current store (defaults to false)
13444      */
13445     doQuery : function(q, forceAll){
13446         
13447         if(q === undefined || q === null){
13448             q = '';
13449         }
13450         var qe = {
13451             query: q,
13452             forceAll: forceAll,
13453             combo: this,
13454             cancel:false
13455         };
13456         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13457             return false;
13458         }
13459         q = qe.query;
13460         
13461         forceAll = qe.forceAll;
13462         if(forceAll === true || (q.length >= this.minChars)){
13463             
13464             this.hasQuery = true;
13465             
13466             if(this.lastQuery != q || this.alwaysQuery){
13467                 this.lastQuery = q;
13468                 if(this.mode == 'local'){
13469                     this.selectedIndex = -1;
13470                     if(forceAll){
13471                         this.store.clearFilter();
13472                     }else{
13473                         
13474                         if(this.specialFilter){
13475                             this.fireEvent('specialfilter', this);
13476                             this.onLoad();
13477                             return;
13478                         }
13479                         
13480                         this.store.filter(this.displayField, q);
13481                     }
13482                     
13483                     this.store.fireEvent("datachanged", this.store);
13484                     
13485                     this.onLoad();
13486                     
13487                     
13488                 }else{
13489                     
13490                     this.store.baseParams[this.queryParam] = q;
13491                     
13492                     var options = {params : this.getParams(q)};
13493                     
13494                     if(this.loadNext){
13495                         options.add = true;
13496                         options.params.start = this.page * this.pageSize;
13497                     }
13498                     
13499                     this.store.load(options);
13500                     
13501                     /*
13502                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13503                      *  we should expand the list on onLoad
13504                      *  so command out it
13505                      */
13506 //                    this.expand();
13507                 }
13508             }else{
13509                 this.selectedIndex = -1;
13510                 this.onLoad();   
13511             }
13512         }
13513         
13514         this.loadNext = false;
13515     },
13516     
13517     // private
13518     getParams : function(q){
13519         var p = {};
13520         //p[this.queryParam] = q;
13521         
13522         if(this.pageSize){
13523             p.start = 0;
13524             p.limit = this.pageSize;
13525         }
13526         return p;
13527     },
13528
13529     /**
13530      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13531      */
13532     collapse : function(){
13533         if(!this.isExpanded()){
13534             return;
13535         }
13536         
13537         this.list.hide();
13538         
13539         if(this.tickable){
13540             this.hasFocus = false;
13541             this.okBtn.hide();
13542             this.cancelBtn.hide();
13543             this.trigger.show();
13544             
13545             if(this.editable){
13546                 this.tickableInputEl().dom.value = '';
13547                 this.tickableInputEl().blur();
13548             }
13549             
13550         }
13551         
13552         Roo.get(document).un('mousedown', this.collapseIf, this);
13553         Roo.get(document).un('mousewheel', this.collapseIf, this);
13554         if (!this.editable) {
13555             Roo.get(document).un('keydown', this.listKeyPress, this);
13556         }
13557         this.fireEvent('collapse', this);
13558         
13559         this.validate();
13560     },
13561
13562     // private
13563     collapseIf : function(e){
13564         var in_combo  = e.within(this.el);
13565         var in_list =  e.within(this.list);
13566         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13567         
13568         if (in_combo || in_list || is_list) {
13569             //e.stopPropagation();
13570             return;
13571         }
13572         
13573         if(this.tickable){
13574             this.onTickableFooterButtonClick(e, false, false);
13575         }
13576
13577         this.collapse();
13578         
13579     },
13580
13581     /**
13582      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13583      */
13584     expand : function(){
13585        
13586         if(this.isExpanded() || !this.hasFocus){
13587             return;
13588         }
13589         
13590         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13591         this.list.setWidth(lw);
13592         
13593         
13594          Roo.log('expand');
13595         
13596         this.list.show();
13597         
13598         this.restrictHeight();
13599         
13600         if(this.tickable){
13601             
13602             this.tickItems = Roo.apply([], this.item);
13603             
13604             this.okBtn.show();
13605             this.cancelBtn.show();
13606             this.trigger.hide();
13607             
13608             if(this.editable){
13609                 this.tickableInputEl().focus();
13610             }
13611             
13612         }
13613         
13614         Roo.get(document).on('mousedown', this.collapseIf, this);
13615         Roo.get(document).on('mousewheel', this.collapseIf, this);
13616         if (!this.editable) {
13617             Roo.get(document).on('keydown', this.listKeyPress, this);
13618         }
13619         
13620         this.fireEvent('expand', this);
13621     },
13622
13623     // private
13624     // Implements the default empty TriggerField.onTriggerClick function
13625     onTriggerClick : function(e)
13626     {
13627         Roo.log('trigger click');
13628         
13629         if(this.disabled || !this.triggerList){
13630             return;
13631         }
13632         
13633         this.page = 0;
13634         this.loadNext = false;
13635         
13636         if(this.isExpanded()){
13637             this.collapse();
13638             if (!this.blockFocus) {
13639                 this.inputEl().focus();
13640             }
13641             
13642         }else {
13643             this.hasFocus = true;
13644             if(this.triggerAction == 'all') {
13645                 this.doQuery(this.allQuery, true);
13646             } else {
13647                 this.doQuery(this.getRawValue());
13648             }
13649             if (!this.blockFocus) {
13650                 this.inputEl().focus();
13651             }
13652         }
13653     },
13654     
13655     onTickableTriggerClick : function(e)
13656     {
13657         if(this.disabled){
13658             return;
13659         }
13660         
13661         this.page = 0;
13662         this.loadNext = false;
13663         this.hasFocus = true;
13664         
13665         if(this.triggerAction == 'all') {
13666             this.doQuery(this.allQuery, true);
13667         } else {
13668             this.doQuery(this.getRawValue());
13669         }
13670     },
13671     
13672     onSearchFieldClick : function(e)
13673     {
13674         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13675             this.onTickableFooterButtonClick(e, false, false);
13676             return;
13677         }
13678         
13679         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13680             return;
13681         }
13682         
13683         this.page = 0;
13684         this.loadNext = false;
13685         this.hasFocus = true;
13686         
13687         if(this.triggerAction == 'all') {
13688             this.doQuery(this.allQuery, true);
13689         } else {
13690             this.doQuery(this.getRawValue());
13691         }
13692     },
13693     
13694     listKeyPress : function(e)
13695     {
13696         //Roo.log('listkeypress');
13697         // scroll to first matching element based on key pres..
13698         if (e.isSpecialKey()) {
13699             return false;
13700         }
13701         var k = String.fromCharCode(e.getKey()).toUpperCase();
13702         //Roo.log(k);
13703         var match  = false;
13704         var csel = this.view.getSelectedNodes();
13705         var cselitem = false;
13706         if (csel.length) {
13707             var ix = this.view.indexOf(csel[0]);
13708             cselitem  = this.store.getAt(ix);
13709             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13710                 cselitem = false;
13711             }
13712             
13713         }
13714         
13715         this.store.each(function(v) { 
13716             if (cselitem) {
13717                 // start at existing selection.
13718                 if (cselitem.id == v.id) {
13719                     cselitem = false;
13720                 }
13721                 return true;
13722             }
13723                 
13724             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13725                 match = this.store.indexOf(v);
13726                 return false;
13727             }
13728             return true;
13729         }, this);
13730         
13731         if (match === false) {
13732             return true; // no more action?
13733         }
13734         // scroll to?
13735         this.view.select(match);
13736         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13737         sn.scrollIntoView(sn.dom.parentNode, false);
13738     },
13739     
13740     onViewScroll : function(e, t){
13741         
13742         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){
13743             return;
13744         }
13745         
13746         this.hasQuery = true;
13747         
13748         this.loading = this.list.select('.loading', true).first();
13749         
13750         if(this.loading === null){
13751             this.list.createChild({
13752                 tag: 'div',
13753                 cls: 'loading roo-select2-more-results roo-select2-active',
13754                 html: 'Loading more results...'
13755             });
13756             
13757             this.loading = this.list.select('.loading', true).first();
13758             
13759             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13760             
13761             this.loading.hide();
13762         }
13763         
13764         this.loading.show();
13765         
13766         var _combo = this;
13767         
13768         this.page++;
13769         this.loadNext = true;
13770         
13771         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13772         
13773         return;
13774     },
13775     
13776     addItem : function(o)
13777     {   
13778         var dv = ''; // display value
13779         
13780         if (this.displayField) {
13781             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13782         } else {
13783             // this is an error condition!!!
13784             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13785         }
13786         
13787         if(!dv.length){
13788             return;
13789         }
13790         
13791         var choice = this.choices.createChild({
13792             tag: 'li',
13793             cls: 'roo-select2-search-choice',
13794             cn: [
13795                 {
13796                     tag: 'div',
13797                     html: dv
13798                 },
13799                 {
13800                     tag: 'a',
13801                     href: '#',
13802                     cls: 'roo-select2-search-choice-close',
13803                     tabindex: '-1'
13804                 }
13805             ]
13806             
13807         }, this.searchField);
13808         
13809         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13810         
13811         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13812         
13813         this.item.push(o);
13814         
13815         this.lastData = o;
13816         
13817         this.syncValue();
13818         
13819         this.inputEl().dom.value = '';
13820         
13821         this.validate();
13822     },
13823     
13824     onRemoveItem : function(e, _self, o)
13825     {
13826         e.preventDefault();
13827         
13828         this.lastItem = Roo.apply([], this.item);
13829         
13830         var index = this.item.indexOf(o.data) * 1;
13831         
13832         if( index < 0){
13833             Roo.log('not this item?!');
13834             return;
13835         }
13836         
13837         this.item.splice(index, 1);
13838         o.item.remove();
13839         
13840         this.syncValue();
13841         
13842         this.fireEvent('remove', this, e);
13843         
13844         this.validate();
13845         
13846     },
13847     
13848     syncValue : function()
13849     {
13850         if(!this.item.length){
13851             this.clearValue();
13852             return;
13853         }
13854             
13855         var value = [];
13856         var _this = this;
13857         Roo.each(this.item, function(i){
13858             if(_this.valueField){
13859                 value.push(i[_this.valueField]);
13860                 return;
13861             }
13862
13863             value.push(i);
13864         });
13865
13866         this.value = value.join(',');
13867
13868         if(this.hiddenField){
13869             this.hiddenField.dom.value = this.value;
13870         }
13871         
13872         this.store.fireEvent("datachanged", this.store);
13873         
13874         this.validate();
13875     },
13876     
13877     clearItem : function()
13878     {
13879         if(!this.multiple){
13880             return;
13881         }
13882         
13883         this.item = [];
13884         
13885         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13886            c.remove();
13887         });
13888         
13889         this.syncValue();
13890         
13891         this.validate();
13892         
13893         if(this.tickable && !Roo.isTouch){
13894             this.view.refresh();
13895         }
13896     },
13897     
13898     inputEl: function ()
13899     {
13900         if(Roo.isTouch && this.mobileTouchView){
13901             return this.el.select('input.form-control',true).first();
13902         }
13903         
13904         if(this.tickable){
13905             return this.searchField;
13906         }
13907         
13908         return this.el.select('input.form-control',true).first();
13909     },
13910     
13911     
13912     onTickableFooterButtonClick : function(e, btn, el)
13913     {
13914         e.preventDefault();
13915         
13916         this.lastItem = Roo.apply([], this.item);
13917         
13918         if(btn && btn.name == 'cancel'){
13919             this.tickItems = Roo.apply([], this.item);
13920             this.collapse();
13921             return;
13922         }
13923         
13924         this.clearItem();
13925         
13926         var _this = this;
13927         
13928         Roo.each(this.tickItems, function(o){
13929             _this.addItem(o);
13930         });
13931         
13932         this.collapse();
13933         
13934     },
13935     
13936     validate : function()
13937     {
13938         var v = this.getRawValue();
13939         
13940         if(this.multiple){
13941             v = this.getValue();
13942         }
13943         
13944         if(this.disabled || this.allowBlank || v.length){
13945             this.markValid();
13946             return true;
13947         }
13948         
13949         this.markInvalid();
13950         return false;
13951     },
13952     
13953     tickableInputEl : function()
13954     {
13955         if(!this.tickable || !this.editable){
13956             return this.inputEl();
13957         }
13958         
13959         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13960     },
13961     
13962     
13963     getAutoCreateTouchView : function()
13964     {
13965         var id = Roo.id();
13966         
13967         var cfg = {
13968             cls: 'form-group' //input-group
13969         };
13970         
13971         var input =  {
13972             tag: 'input',
13973             id : id,
13974             type : this.inputType,
13975             cls : 'form-control x-combo-noedit',
13976             autocomplete: 'new-password',
13977             placeholder : this.placeholder || '',
13978             readonly : true
13979         };
13980         
13981         if (this.name) {
13982             input.name = this.name;
13983         }
13984         
13985         if (this.size) {
13986             input.cls += ' input-' + this.size;
13987         }
13988         
13989         if (this.disabled) {
13990             input.disabled = true;
13991         }
13992         
13993         var inputblock = {
13994             cls : '',
13995             cn : [
13996                 input
13997             ]
13998         };
13999         
14000         if(this.before){
14001             inputblock.cls += ' input-group';
14002             
14003             inputblock.cn.unshift({
14004                 tag :'span',
14005                 cls : 'input-group-addon',
14006                 html : this.before
14007             });
14008         }
14009         
14010         if(this.removable && !this.multiple){
14011             inputblock.cls += ' roo-removable';
14012             
14013             inputblock.cn.push({
14014                 tag: 'button',
14015                 html : 'x',
14016                 cls : 'roo-combo-removable-btn close'
14017             });
14018         }
14019
14020         if(this.hasFeedback && !this.allowBlank){
14021             
14022             inputblock.cls += ' has-feedback';
14023             
14024             inputblock.cn.push({
14025                 tag: 'span',
14026                 cls: 'glyphicon form-control-feedback'
14027             });
14028             
14029         }
14030         
14031         if (this.after) {
14032             
14033             inputblock.cls += (this.before) ? '' : ' input-group';
14034             
14035             inputblock.cn.push({
14036                 tag :'span',
14037                 cls : 'input-group-addon',
14038                 html : this.after
14039             });
14040         }
14041
14042         var box = {
14043             tag: 'div',
14044             cn: [
14045                 {
14046                     tag: 'input',
14047                     type : 'hidden',
14048                     cls: 'form-hidden-field'
14049                 },
14050                 inputblock
14051             ]
14052             
14053         };
14054         
14055         if(this.multiple){
14056             box = {
14057                 tag: 'div',
14058                 cn: [
14059                     {
14060                         tag: 'input',
14061                         type : 'hidden',
14062                         cls: 'form-hidden-field'
14063                     },
14064                     {
14065                         tag: 'ul',
14066                         cls: 'roo-select2-choices',
14067                         cn:[
14068                             {
14069                                 tag: 'li',
14070                                 cls: 'roo-select2-search-field',
14071                                 cn: [
14072
14073                                     inputblock
14074                                 ]
14075                             }
14076                         ]
14077                     }
14078                 ]
14079             }
14080         };
14081         
14082         var combobox = {
14083             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14084             cn: [
14085                 box
14086             ]
14087         };
14088         
14089         if(!this.multiple && this.showToggleBtn){
14090             
14091             var caret = {
14092                         tag: 'span',
14093                         cls: 'caret'
14094             };
14095             
14096             if (this.caret != false) {
14097                 caret = {
14098                      tag: 'i',
14099                      cls: 'fa fa-' + this.caret
14100                 };
14101                 
14102             }
14103             
14104             combobox.cn.push({
14105                 tag :'span',
14106                 cls : 'input-group-addon btn dropdown-toggle',
14107                 cn : [
14108                     caret,
14109                     {
14110                         tag: 'span',
14111                         cls: 'combobox-clear',
14112                         cn  : [
14113                             {
14114                                 tag : 'i',
14115                                 cls: 'icon-remove'
14116                             }
14117                         ]
14118                     }
14119                 ]
14120
14121             })
14122         }
14123         
14124         if(this.multiple){
14125             combobox.cls += ' roo-select2-container-multi';
14126         }
14127         
14128         var align = this.labelAlign || this.parentLabelAlign();
14129         
14130         cfg.cn = combobox;
14131         
14132         if(this.fieldLabel.length && this.labelWidth){
14133             
14134             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14135             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14136             
14137             cfg.cn = [
14138                 {
14139                    tag : 'i',
14140                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14141                    tooltip : 'This field is required'
14142                 },
14143                 {
14144                     tag: 'label',
14145                     cls : 'control-label ' + lw,
14146                     html : this.fieldLabel
14147
14148                 },
14149                 {
14150                     cls : cw, 
14151                     cn: [
14152                         combobox
14153                     ]
14154                 }
14155             ];
14156             
14157             if(this.indicatorpos == 'right'){
14158                 cfg.cn = [
14159                     {
14160                         tag: 'label',
14161                         cls : 'control-label ' + lw,
14162                         html : this.fieldLabel
14163
14164                     },
14165                     {
14166                        tag : 'i',
14167                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14168                        tooltip : 'This field is required'
14169                     },
14170                     {
14171                         cls : cw, 
14172                         cn: [
14173                             combobox
14174                         ]
14175                     }
14176                 ];
14177             }
14178         }
14179         
14180         var settings = this;
14181         
14182         ['xs','sm','md','lg'].map(function(size){
14183             if (settings[size]) {
14184                 cfg.cls += ' col-' + size + '-' + settings[size];
14185             }
14186         });
14187         
14188         return cfg;
14189     },
14190     
14191     initTouchView : function()
14192     {
14193         this.renderTouchView();
14194         
14195         this.touchViewEl.on('scroll', function(){
14196             this.el.dom.scrollTop = 0;
14197         }, this);
14198         
14199         this.originalValue = this.getValue();
14200         
14201         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14202         
14203         this.inputEl().on("click", this.showTouchView, this);
14204         this.triggerEl.on("click", this.showTouchView, this);
14205         
14206         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14207         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14208         
14209         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14210         
14211         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14212         this.store.on('load', this.onTouchViewLoad, this);
14213         this.store.on('loadexception', this.onTouchViewLoadException, this);
14214         
14215         if(this.hiddenName){
14216             
14217             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14218             
14219             this.hiddenField.dom.value =
14220                 this.hiddenValue !== undefined ? this.hiddenValue :
14221                 this.value !== undefined ? this.value : '';
14222         
14223             this.el.dom.removeAttribute('name');
14224             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14225         }
14226         
14227         if(this.multiple){
14228             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14229             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14230         }
14231         
14232         if(this.removable && !this.multiple){
14233             var close = this.closeTriggerEl();
14234             if(close){
14235                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14236                 close.on('click', this.removeBtnClick, this, close);
14237             }
14238         }
14239         /*
14240          * fix the bug in Safari iOS8
14241          */
14242         this.inputEl().on("focus", function(e){
14243             document.activeElement.blur();
14244         }, this);
14245         
14246         return;
14247         
14248         
14249     },
14250     
14251     renderTouchView : function()
14252     {
14253         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14254         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14255         
14256         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14257         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14258         
14259         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14260         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14261         this.touchViewBodyEl.setStyle('overflow', 'auto');
14262         
14263         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14264         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14265         
14266         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14267         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14268         
14269     },
14270     
14271     showTouchView : function()
14272     {
14273         if(this.disabled){
14274             return;
14275         }
14276         
14277         this.touchViewHeaderEl.hide();
14278
14279         if(this.modalTitle.length){
14280             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14281             this.touchViewHeaderEl.show();
14282         }
14283
14284         this.touchViewEl.show();
14285
14286         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14287         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14288                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14289
14290         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14291
14292         if(this.modalTitle.length){
14293             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14294         }
14295         
14296         this.touchViewBodyEl.setHeight(bodyHeight);
14297
14298         if(this.animate){
14299             var _this = this;
14300             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14301         }else{
14302             this.touchViewEl.addClass('in');
14303         }
14304
14305         this.doTouchViewQuery();
14306         
14307     },
14308     
14309     hideTouchView : function()
14310     {
14311         this.touchViewEl.removeClass('in');
14312
14313         if(this.animate){
14314             var _this = this;
14315             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14316         }else{
14317             this.touchViewEl.setStyle('display', 'none');
14318         }
14319         
14320     },
14321     
14322     setTouchViewValue : function()
14323     {
14324         if(this.multiple){
14325             this.clearItem();
14326         
14327             var _this = this;
14328
14329             Roo.each(this.tickItems, function(o){
14330                 this.addItem(o);
14331             }, this);
14332         }
14333         
14334         this.hideTouchView();
14335     },
14336     
14337     doTouchViewQuery : function()
14338     {
14339         var qe = {
14340             query: '',
14341             forceAll: true,
14342             combo: this,
14343             cancel:false
14344         };
14345         
14346         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14347             return false;
14348         }
14349         
14350         if(!this.alwaysQuery || this.mode == 'local'){
14351             this.onTouchViewLoad();
14352             return;
14353         }
14354         
14355         this.store.load();
14356     },
14357     
14358     onTouchViewBeforeLoad : function(combo,opts)
14359     {
14360         return;
14361     },
14362
14363     // private
14364     onTouchViewLoad : function()
14365     {
14366         if(this.store.getCount() < 1){
14367             this.onTouchViewEmptyResults();
14368             return;
14369         }
14370         
14371         this.clearTouchView();
14372         
14373         var rawValue = this.getRawValue();
14374         
14375         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14376         
14377         this.tickItems = [];
14378         
14379         this.store.data.each(function(d, rowIndex){
14380             var row = this.touchViewListGroup.createChild(template);
14381             
14382             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14383                 row.addClass(d.data.cls);
14384             }
14385             
14386             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14387                 var cfg = {
14388                     data : d.data,
14389                     html : d.data[this.displayField]
14390                 };
14391                 
14392                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14393                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14394                 }
14395             }
14396             row.removeClass('selected');
14397             if(!this.multiple && this.valueField &&
14398                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14399             {
14400                 // radio buttons..
14401                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14402                 row.addClass('selected');
14403             }
14404             
14405             if(this.multiple && this.valueField &&
14406                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14407             {
14408                 
14409                 // checkboxes...
14410                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14411                 this.tickItems.push(d.data);
14412             }
14413             
14414             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14415             
14416         }, this);
14417         
14418         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14419         
14420         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14421
14422         if(this.modalTitle.length){
14423             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14424         }
14425
14426         var listHeight = this.touchViewListGroup.getHeight();
14427         
14428         var _this = this;
14429         
14430         if(firstChecked && listHeight > bodyHeight){
14431             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14432         }
14433         
14434     },
14435     
14436     onTouchViewLoadException : function()
14437     {
14438         this.hideTouchView();
14439     },
14440     
14441     onTouchViewEmptyResults : function()
14442     {
14443         this.clearTouchView();
14444         
14445         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14446         
14447         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14448         
14449     },
14450     
14451     clearTouchView : function()
14452     {
14453         this.touchViewListGroup.dom.innerHTML = '';
14454     },
14455     
14456     onTouchViewClick : function(e, el, o)
14457     {
14458         e.preventDefault();
14459         
14460         var row = o.row;
14461         var rowIndex = o.rowIndex;
14462         
14463         var r = this.store.getAt(rowIndex);
14464         
14465         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14466             
14467             if(!this.multiple){
14468                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14469                     c.dom.removeAttribute('checked');
14470                 }, this);
14471
14472                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14473
14474                 this.setFromData(r.data);
14475
14476                 var close = this.closeTriggerEl();
14477
14478                 if(close){
14479                     close.show();
14480                 }
14481
14482                 this.hideTouchView();
14483
14484                 this.fireEvent('select', this, r, rowIndex);
14485
14486                 return;
14487             }
14488
14489             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14490                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14491                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14492                 return;
14493             }
14494
14495             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14496             this.addItem(r.data);
14497             this.tickItems.push(r.data);
14498         }
14499     }
14500     
14501
14502     /** 
14503     * @cfg {Boolean} grow 
14504     * @hide 
14505     */
14506     /** 
14507     * @cfg {Number} growMin 
14508     * @hide 
14509     */
14510     /** 
14511     * @cfg {Number} growMax 
14512     * @hide 
14513     */
14514     /**
14515      * @hide
14516      * @method autoSize
14517      */
14518 });
14519
14520 Roo.apply(Roo.bootstrap.ComboBox,  {
14521     
14522     header : {
14523         tag: 'div',
14524         cls: 'modal-header',
14525         cn: [
14526             {
14527                 tag: 'h4',
14528                 cls: 'modal-title'
14529             }
14530         ]
14531     },
14532     
14533     body : {
14534         tag: 'div',
14535         cls: 'modal-body',
14536         cn: [
14537             {
14538                 tag: 'ul',
14539                 cls: 'list-group'
14540             }
14541         ]
14542     },
14543     
14544     listItemRadio : {
14545         tag: 'li',
14546         cls: 'list-group-item',
14547         cn: [
14548             {
14549                 tag: 'span',
14550                 cls: 'roo-combobox-list-group-item-value'
14551             },
14552             {
14553                 tag: 'div',
14554                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14555                 cn: [
14556                     {
14557                         tag: 'input',
14558                         type: 'radio'
14559                     },
14560                     {
14561                         tag: 'label'
14562                     }
14563                 ]
14564             }
14565         ]
14566     },
14567     
14568     listItemCheckbox : {
14569         tag: 'li',
14570         cls: 'list-group-item',
14571         cn: [
14572             {
14573                 tag: 'span',
14574                 cls: 'roo-combobox-list-group-item-value'
14575             },
14576             {
14577                 tag: 'div',
14578                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14579                 cn: [
14580                     {
14581                         tag: 'input',
14582                         type: 'checkbox'
14583                     },
14584                     {
14585                         tag: 'label'
14586                     }
14587                 ]
14588             }
14589         ]
14590     },
14591     
14592     emptyResult : {
14593         tag: 'div',
14594         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14595     },
14596     
14597     footer : {
14598         tag: 'div',
14599         cls: 'modal-footer',
14600         cn: [
14601             {
14602                 tag: 'div',
14603                 cls: 'row',
14604                 cn: [
14605                     {
14606                         tag: 'div',
14607                         cls: 'col-xs-6 text-left',
14608                         cn: {
14609                             tag: 'button',
14610                             cls: 'btn btn-danger roo-touch-view-cancel',
14611                             html: 'Cancel'
14612                         }
14613                     },
14614                     {
14615                         tag: 'div',
14616                         cls: 'col-xs-6 text-right',
14617                         cn: {
14618                             tag: 'button',
14619                             cls: 'btn btn-success roo-touch-view-ok',
14620                             html: 'OK'
14621                         }
14622                     }
14623                 ]
14624             }
14625         ]
14626         
14627     }
14628 });
14629
14630 Roo.apply(Roo.bootstrap.ComboBox,  {
14631     
14632     touchViewTemplate : {
14633         tag: 'div',
14634         cls: 'modal fade roo-combobox-touch-view',
14635         cn: [
14636             {
14637                 tag: 'div',
14638                 cls: 'modal-dialog',
14639                 style : 'position:fixed', // we have to fix position....
14640                 cn: [
14641                     {
14642                         tag: 'div',
14643                         cls: 'modal-content',
14644                         cn: [
14645                             Roo.bootstrap.ComboBox.header,
14646                             Roo.bootstrap.ComboBox.body,
14647                             Roo.bootstrap.ComboBox.footer
14648                         ]
14649                     }
14650                 ]
14651             }
14652         ]
14653     }
14654 });/*
14655  * Based on:
14656  * Ext JS Library 1.1.1
14657  * Copyright(c) 2006-2007, Ext JS, LLC.
14658  *
14659  * Originally Released Under LGPL - original licence link has changed is not relivant.
14660  *
14661  * Fork - LGPL
14662  * <script type="text/javascript">
14663  */
14664
14665 /**
14666  * @class Roo.View
14667  * @extends Roo.util.Observable
14668  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14669  * This class also supports single and multi selection modes. <br>
14670  * Create a data model bound view:
14671  <pre><code>
14672  var store = new Roo.data.Store(...);
14673
14674  var view = new Roo.View({
14675     el : "my-element",
14676     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14677  
14678     singleSelect: true,
14679     selectedClass: "ydataview-selected",
14680     store: store
14681  });
14682
14683  // listen for node click?
14684  view.on("click", function(vw, index, node, e){
14685  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14686  });
14687
14688  // load XML data
14689  dataModel.load("foobar.xml");
14690  </code></pre>
14691  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14692  * <br><br>
14693  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14694  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14695  * 
14696  * Note: old style constructor is still suported (container, template, config)
14697  * 
14698  * @constructor
14699  * Create a new View
14700  * @param {Object} config The config object
14701  * 
14702  */
14703 Roo.View = function(config, depreciated_tpl, depreciated_config){
14704     
14705     this.parent = false;
14706     
14707     if (typeof(depreciated_tpl) == 'undefined') {
14708         // new way.. - universal constructor.
14709         Roo.apply(this, config);
14710         this.el  = Roo.get(this.el);
14711     } else {
14712         // old format..
14713         this.el  = Roo.get(config);
14714         this.tpl = depreciated_tpl;
14715         Roo.apply(this, depreciated_config);
14716     }
14717     this.wrapEl  = this.el.wrap().wrap();
14718     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14719     
14720     
14721     if(typeof(this.tpl) == "string"){
14722         this.tpl = new Roo.Template(this.tpl);
14723     } else {
14724         // support xtype ctors..
14725         this.tpl = new Roo.factory(this.tpl, Roo);
14726     }
14727     
14728     
14729     this.tpl.compile();
14730     
14731     /** @private */
14732     this.addEvents({
14733         /**
14734          * @event beforeclick
14735          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
14742         /**
14743          * @event click
14744          * Fires when a template node is 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             "click" : true,
14751         /**
14752          * @event dblclick
14753          * Fires when a template node is double clicked.
14754          * @param {Roo.View} this
14755          * @param {Number} index The index of the target node
14756          * @param {HTMLElement} node The target node
14757          * @param {Roo.EventObject} e The raw event object
14758          */
14759             "dblclick" : true,
14760         /**
14761          * @event contextmenu
14762          * Fires when a template node is right clicked.
14763          * @param {Roo.View} this
14764          * @param {Number} index The index of the target node
14765          * @param {HTMLElement} node The target node
14766          * @param {Roo.EventObject} e The raw event object
14767          */
14768             "contextmenu" : true,
14769         /**
14770          * @event selectionchange
14771          * Fires when the selected nodes change.
14772          * @param {Roo.View} this
14773          * @param {Array} selections Array of the selected nodes
14774          */
14775             "selectionchange" : true,
14776     
14777         /**
14778          * @event beforeselect
14779          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14780          * @param {Roo.View} this
14781          * @param {HTMLElement} node The node to be selected
14782          * @param {Array} selections Array of currently selected nodes
14783          */
14784             "beforeselect" : true,
14785         /**
14786          * @event preparedata
14787          * Fires on every row to render, to allow you to change the data.
14788          * @param {Roo.View} this
14789          * @param {Object} data to be rendered (change this)
14790          */
14791           "preparedata" : true
14792           
14793           
14794         });
14795
14796
14797
14798     this.el.on({
14799         "click": this.onClick,
14800         "dblclick": this.onDblClick,
14801         "contextmenu": this.onContextMenu,
14802         scope:this
14803     });
14804
14805     this.selections = [];
14806     this.nodes = [];
14807     this.cmp = new Roo.CompositeElementLite([]);
14808     if(this.store){
14809         this.store = Roo.factory(this.store, Roo.data);
14810         this.setStore(this.store, true);
14811     }
14812     
14813     if ( this.footer && this.footer.xtype) {
14814            
14815          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14816         
14817         this.footer.dataSource = this.store;
14818         this.footer.container = fctr;
14819         this.footer = Roo.factory(this.footer, Roo);
14820         fctr.insertFirst(this.el);
14821         
14822         // this is a bit insane - as the paging toolbar seems to detach the el..
14823 //        dom.parentNode.parentNode.parentNode
14824          // they get detached?
14825     }
14826     
14827     
14828     Roo.View.superclass.constructor.call(this);
14829     
14830     
14831 };
14832
14833 Roo.extend(Roo.View, Roo.util.Observable, {
14834     
14835      /**
14836      * @cfg {Roo.data.Store} store Data store to load data from.
14837      */
14838     store : false,
14839     
14840     /**
14841      * @cfg {String|Roo.Element} el The container element.
14842      */
14843     el : '',
14844     
14845     /**
14846      * @cfg {String|Roo.Template} tpl The template used by this View 
14847      */
14848     tpl : false,
14849     /**
14850      * @cfg {String} dataName the named area of the template to use as the data area
14851      *                          Works with domtemplates roo-name="name"
14852      */
14853     dataName: false,
14854     /**
14855      * @cfg {String} selectedClass The css class to add to selected nodes
14856      */
14857     selectedClass : "x-view-selected",
14858      /**
14859      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14860      */
14861     emptyText : "",
14862     
14863     /**
14864      * @cfg {String} text to display on mask (default Loading)
14865      */
14866     mask : false,
14867     /**
14868      * @cfg {Boolean} multiSelect Allow multiple selection
14869      */
14870     multiSelect : false,
14871     /**
14872      * @cfg {Boolean} singleSelect Allow single selection
14873      */
14874     singleSelect:  false,
14875     
14876     /**
14877      * @cfg {Boolean} toggleSelect - selecting 
14878      */
14879     toggleSelect : false,
14880     
14881     /**
14882      * @cfg {Boolean} tickable - selecting 
14883      */
14884     tickable : false,
14885     
14886     /**
14887      * Returns the element this view is bound to.
14888      * @return {Roo.Element}
14889      */
14890     getEl : function(){
14891         return this.wrapEl;
14892     },
14893     
14894     
14895
14896     /**
14897      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14898      */
14899     refresh : function(){
14900         //Roo.log('refresh');
14901         var t = this.tpl;
14902         
14903         // if we are using something like 'domtemplate', then
14904         // the what gets used is:
14905         // t.applySubtemplate(NAME, data, wrapping data..)
14906         // the outer template then get' applied with
14907         //     the store 'extra data'
14908         // and the body get's added to the
14909         //      roo-name="data" node?
14910         //      <span class='roo-tpl-{name}'></span> ?????
14911         
14912         
14913         
14914         this.clearSelections();
14915         this.el.update("");
14916         var html = [];
14917         var records = this.store.getRange();
14918         if(records.length < 1) {
14919             
14920             // is this valid??  = should it render a template??
14921             
14922             this.el.update(this.emptyText);
14923             return;
14924         }
14925         var el = this.el;
14926         if (this.dataName) {
14927             this.el.update(t.apply(this.store.meta)); //????
14928             el = this.el.child('.roo-tpl-' + this.dataName);
14929         }
14930         
14931         for(var i = 0, len = records.length; i < len; i++){
14932             var data = this.prepareData(records[i].data, i, records[i]);
14933             this.fireEvent("preparedata", this, data, i, records[i]);
14934             
14935             var d = Roo.apply({}, data);
14936             
14937             if(this.tickable){
14938                 Roo.apply(d, {'roo-id' : Roo.id()});
14939                 
14940                 var _this = this;
14941             
14942                 Roo.each(this.parent.item, function(item){
14943                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14944                         return;
14945                     }
14946                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14947                 });
14948             }
14949             
14950             html[html.length] = Roo.util.Format.trim(
14951                 this.dataName ?
14952                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14953                     t.apply(d)
14954             );
14955         }
14956         
14957         
14958         
14959         el.update(html.join(""));
14960         this.nodes = el.dom.childNodes;
14961         this.updateIndexes(0);
14962     },
14963     
14964
14965     /**
14966      * Function to override to reformat the data that is sent to
14967      * the template for each node.
14968      * DEPRICATED - use the preparedata event handler.
14969      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14970      * a JSON object for an UpdateManager bound view).
14971      */
14972     prepareData : function(data, index, record)
14973     {
14974         this.fireEvent("preparedata", this, data, index, record);
14975         return data;
14976     },
14977
14978     onUpdate : function(ds, record){
14979         // Roo.log('on update');   
14980         this.clearSelections();
14981         var index = this.store.indexOf(record);
14982         var n = this.nodes[index];
14983         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14984         n.parentNode.removeChild(n);
14985         this.updateIndexes(index, index);
14986     },
14987
14988     
14989     
14990 // --------- FIXME     
14991     onAdd : function(ds, records, index)
14992     {
14993         //Roo.log(['on Add', ds, records, index] );        
14994         this.clearSelections();
14995         if(this.nodes.length == 0){
14996             this.refresh();
14997             return;
14998         }
14999         var n = this.nodes[index];
15000         for(var i = 0, len = records.length; i < len; i++){
15001             var d = this.prepareData(records[i].data, i, records[i]);
15002             if(n){
15003                 this.tpl.insertBefore(n, d);
15004             }else{
15005                 
15006                 this.tpl.append(this.el, d);
15007             }
15008         }
15009         this.updateIndexes(index);
15010     },
15011
15012     onRemove : function(ds, record, index){
15013        // Roo.log('onRemove');
15014         this.clearSelections();
15015         var el = this.dataName  ?
15016             this.el.child('.roo-tpl-' + this.dataName) :
15017             this.el; 
15018         
15019         el.dom.removeChild(this.nodes[index]);
15020         this.updateIndexes(index);
15021     },
15022
15023     /**
15024      * Refresh an individual node.
15025      * @param {Number} index
15026      */
15027     refreshNode : function(index){
15028         this.onUpdate(this.store, this.store.getAt(index));
15029     },
15030
15031     updateIndexes : function(startIndex, endIndex){
15032         var ns = this.nodes;
15033         startIndex = startIndex || 0;
15034         endIndex = endIndex || ns.length - 1;
15035         for(var i = startIndex; i <= endIndex; i++){
15036             ns[i].nodeIndex = i;
15037         }
15038     },
15039
15040     /**
15041      * Changes the data store this view uses and refresh the view.
15042      * @param {Store} store
15043      */
15044     setStore : function(store, initial){
15045         if(!initial && this.store){
15046             this.store.un("datachanged", this.refresh);
15047             this.store.un("add", this.onAdd);
15048             this.store.un("remove", this.onRemove);
15049             this.store.un("update", this.onUpdate);
15050             this.store.un("clear", this.refresh);
15051             this.store.un("beforeload", this.onBeforeLoad);
15052             this.store.un("load", this.onLoad);
15053             this.store.un("loadexception", this.onLoad);
15054         }
15055         if(store){
15056           
15057             store.on("datachanged", this.refresh, this);
15058             store.on("add", this.onAdd, this);
15059             store.on("remove", this.onRemove, this);
15060             store.on("update", this.onUpdate, this);
15061             store.on("clear", this.refresh, this);
15062             store.on("beforeload", this.onBeforeLoad, this);
15063             store.on("load", this.onLoad, this);
15064             store.on("loadexception", this.onLoad, this);
15065         }
15066         
15067         if(store){
15068             this.refresh();
15069         }
15070     },
15071     /**
15072      * onbeforeLoad - masks the loading area.
15073      *
15074      */
15075     onBeforeLoad : function(store,opts)
15076     {
15077          //Roo.log('onBeforeLoad');   
15078         if (!opts.add) {
15079             this.el.update("");
15080         }
15081         this.el.mask(this.mask ? this.mask : "Loading" ); 
15082     },
15083     onLoad : function ()
15084     {
15085         this.el.unmask();
15086     },
15087     
15088
15089     /**
15090      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15091      * @param {HTMLElement} node
15092      * @return {HTMLElement} The template node
15093      */
15094     findItemFromChild : function(node){
15095         var el = this.dataName  ?
15096             this.el.child('.roo-tpl-' + this.dataName,true) :
15097             this.el.dom; 
15098         
15099         if(!node || node.parentNode == el){
15100                     return node;
15101             }
15102             var p = node.parentNode;
15103             while(p && p != el){
15104             if(p.parentNode == el){
15105                 return p;
15106             }
15107             p = p.parentNode;
15108         }
15109             return null;
15110     },
15111
15112     /** @ignore */
15113     onClick : function(e){
15114         var item = this.findItemFromChild(e.getTarget());
15115         if(item){
15116             var index = this.indexOf(item);
15117             if(this.onItemClick(item, index, e) !== false){
15118                 this.fireEvent("click", this, index, item, e);
15119             }
15120         }else{
15121             this.clearSelections();
15122         }
15123     },
15124
15125     /** @ignore */
15126     onContextMenu : function(e){
15127         var item = this.findItemFromChild(e.getTarget());
15128         if(item){
15129             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15130         }
15131     },
15132
15133     /** @ignore */
15134     onDblClick : function(e){
15135         var item = this.findItemFromChild(e.getTarget());
15136         if(item){
15137             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15138         }
15139     },
15140
15141     onItemClick : function(item, index, e)
15142     {
15143         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15144             return false;
15145         }
15146         if (this.toggleSelect) {
15147             var m = this.isSelected(item) ? 'unselect' : 'select';
15148             //Roo.log(m);
15149             var _t = this;
15150             _t[m](item, true, false);
15151             return true;
15152         }
15153         if(this.multiSelect || this.singleSelect){
15154             if(this.multiSelect && e.shiftKey && this.lastSelection){
15155                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15156             }else{
15157                 this.select(item, this.multiSelect && e.ctrlKey);
15158                 this.lastSelection = item;
15159             }
15160             
15161             if(!this.tickable){
15162                 e.preventDefault();
15163             }
15164             
15165         }
15166         return true;
15167     },
15168
15169     /**
15170      * Get the number of selected nodes.
15171      * @return {Number}
15172      */
15173     getSelectionCount : function(){
15174         return this.selections.length;
15175     },
15176
15177     /**
15178      * Get the currently selected nodes.
15179      * @return {Array} An array of HTMLElements
15180      */
15181     getSelectedNodes : function(){
15182         return this.selections;
15183     },
15184
15185     /**
15186      * Get the indexes of the selected nodes.
15187      * @return {Array}
15188      */
15189     getSelectedIndexes : function(){
15190         var indexes = [], s = this.selections;
15191         for(var i = 0, len = s.length; i < len; i++){
15192             indexes.push(s[i].nodeIndex);
15193         }
15194         return indexes;
15195     },
15196
15197     /**
15198      * Clear all selections
15199      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15200      */
15201     clearSelections : function(suppressEvent){
15202         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15203             this.cmp.elements = this.selections;
15204             this.cmp.removeClass(this.selectedClass);
15205             this.selections = [];
15206             if(!suppressEvent){
15207                 this.fireEvent("selectionchange", this, this.selections);
15208             }
15209         }
15210     },
15211
15212     /**
15213      * Returns true if the passed node is selected
15214      * @param {HTMLElement/Number} node The node or node index
15215      * @return {Boolean}
15216      */
15217     isSelected : function(node){
15218         var s = this.selections;
15219         if(s.length < 1){
15220             return false;
15221         }
15222         node = this.getNode(node);
15223         return s.indexOf(node) !== -1;
15224     },
15225
15226     /**
15227      * Selects nodes.
15228      * @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
15229      * @param {Boolean} keepExisting (optional) true to keep existing selections
15230      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15231      */
15232     select : function(nodeInfo, keepExisting, suppressEvent){
15233         if(nodeInfo instanceof Array){
15234             if(!keepExisting){
15235                 this.clearSelections(true);
15236             }
15237             for(var i = 0, len = nodeInfo.length; i < len; i++){
15238                 this.select(nodeInfo[i], true, true);
15239             }
15240             return;
15241         } 
15242         var node = this.getNode(nodeInfo);
15243         if(!node || this.isSelected(node)){
15244             return; // already selected.
15245         }
15246         if(!keepExisting){
15247             this.clearSelections(true);
15248         }
15249         
15250         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15251             Roo.fly(node).addClass(this.selectedClass);
15252             this.selections.push(node);
15253             if(!suppressEvent){
15254                 this.fireEvent("selectionchange", this, this.selections);
15255             }
15256         }
15257         
15258         
15259     },
15260       /**
15261      * Unselects nodes.
15262      * @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
15263      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15264      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15265      */
15266     unselect : function(nodeInfo, keepExisting, suppressEvent)
15267     {
15268         if(nodeInfo instanceof Array){
15269             Roo.each(this.selections, function(s) {
15270                 this.unselect(s, nodeInfo);
15271             }, this);
15272             return;
15273         }
15274         var node = this.getNode(nodeInfo);
15275         if(!node || !this.isSelected(node)){
15276             //Roo.log("not selected");
15277             return; // not selected.
15278         }
15279         // fireevent???
15280         var ns = [];
15281         Roo.each(this.selections, function(s) {
15282             if (s == node ) {
15283                 Roo.fly(node).removeClass(this.selectedClass);
15284
15285                 return;
15286             }
15287             ns.push(s);
15288         },this);
15289         
15290         this.selections= ns;
15291         this.fireEvent("selectionchange", this, this.selections);
15292     },
15293
15294     /**
15295      * Gets a template node.
15296      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15297      * @return {HTMLElement} The node or null if it wasn't found
15298      */
15299     getNode : function(nodeInfo){
15300         if(typeof nodeInfo == "string"){
15301             return document.getElementById(nodeInfo);
15302         }else if(typeof nodeInfo == "number"){
15303             return this.nodes[nodeInfo];
15304         }
15305         return nodeInfo;
15306     },
15307
15308     /**
15309      * Gets a range template nodes.
15310      * @param {Number} startIndex
15311      * @param {Number} endIndex
15312      * @return {Array} An array of nodes
15313      */
15314     getNodes : function(start, end){
15315         var ns = this.nodes;
15316         start = start || 0;
15317         end = typeof end == "undefined" ? ns.length - 1 : end;
15318         var nodes = [];
15319         if(start <= end){
15320             for(var i = start; i <= end; i++){
15321                 nodes.push(ns[i]);
15322             }
15323         } else{
15324             for(var i = start; i >= end; i--){
15325                 nodes.push(ns[i]);
15326             }
15327         }
15328         return nodes;
15329     },
15330
15331     /**
15332      * Finds the index of the passed node
15333      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15334      * @return {Number} The index of the node or -1
15335      */
15336     indexOf : function(node){
15337         node = this.getNode(node);
15338         if(typeof node.nodeIndex == "number"){
15339             return node.nodeIndex;
15340         }
15341         var ns = this.nodes;
15342         for(var i = 0, len = ns.length; i < len; i++){
15343             if(ns[i] == node){
15344                 return i;
15345             }
15346         }
15347         return -1;
15348     }
15349 });
15350 /*
15351  * - LGPL
15352  *
15353  * based on jquery fullcalendar
15354  * 
15355  */
15356
15357 Roo.bootstrap = Roo.bootstrap || {};
15358 /**
15359  * @class Roo.bootstrap.Calendar
15360  * @extends Roo.bootstrap.Component
15361  * Bootstrap Calendar class
15362  * @cfg {Boolean} loadMask (true|false) default false
15363  * @cfg {Object} header generate the user specific header of the calendar, default false
15364
15365  * @constructor
15366  * Create a new Container
15367  * @param {Object} config The config object
15368  */
15369
15370
15371
15372 Roo.bootstrap.Calendar = function(config){
15373     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15374      this.addEvents({
15375         /**
15376              * @event select
15377              * Fires when a date is selected
15378              * @param {DatePicker} this
15379              * @param {Date} date The selected date
15380              */
15381         'select': true,
15382         /**
15383              * @event monthchange
15384              * Fires when the displayed month changes 
15385              * @param {DatePicker} this
15386              * @param {Date} date The selected month
15387              */
15388         'monthchange': true,
15389         /**
15390              * @event evententer
15391              * Fires when mouse over an event
15392              * @param {Calendar} this
15393              * @param {event} Event
15394              */
15395         'evententer': true,
15396         /**
15397              * @event eventleave
15398              * Fires when the mouse leaves an
15399              * @param {Calendar} this
15400              * @param {event}
15401              */
15402         'eventleave': true,
15403         /**
15404              * @event eventclick
15405              * Fires when the mouse click an
15406              * @param {Calendar} this
15407              * @param {event}
15408              */
15409         'eventclick': true
15410         
15411     });
15412
15413 };
15414
15415 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15416     
15417      /**
15418      * @cfg {Number} startDay
15419      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15420      */
15421     startDay : 0,
15422     
15423     loadMask : false,
15424     
15425     header : false,
15426       
15427     getAutoCreate : function(){
15428         
15429         
15430         var fc_button = function(name, corner, style, content ) {
15431             return Roo.apply({},{
15432                 tag : 'span',
15433                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15434                          (corner.length ?
15435                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15436                             ''
15437                         ),
15438                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15439                 unselectable: 'on'
15440             });
15441         };
15442         
15443         var header = {};
15444         
15445         if(!this.header){
15446             header = {
15447                 tag : 'table',
15448                 cls : 'fc-header',
15449                 style : 'width:100%',
15450                 cn : [
15451                     {
15452                         tag: 'tr',
15453                         cn : [
15454                             {
15455                                 tag : 'td',
15456                                 cls : 'fc-header-left',
15457                                 cn : [
15458                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15459                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15460                                     { tag: 'span', cls: 'fc-header-space' },
15461                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15462
15463
15464                                 ]
15465                             },
15466
15467                             {
15468                                 tag : 'td',
15469                                 cls : 'fc-header-center',
15470                                 cn : [
15471                                     {
15472                                         tag: 'span',
15473                                         cls: 'fc-header-title',
15474                                         cn : {
15475                                             tag: 'H2',
15476                                             html : 'month / year'
15477                                         }
15478                                     }
15479
15480                                 ]
15481                             },
15482                             {
15483                                 tag : 'td',
15484                                 cls : 'fc-header-right',
15485                                 cn : [
15486                               /*      fc_button('month', 'left', '', 'month' ),
15487                                     fc_button('week', '', '', 'week' ),
15488                                     fc_button('day', 'right', '', 'day' )
15489                                 */    
15490
15491                                 ]
15492                             }
15493
15494                         ]
15495                     }
15496                 ]
15497             };
15498         }
15499         
15500         header = this.header;
15501         
15502        
15503         var cal_heads = function() {
15504             var ret = [];
15505             // fixme - handle this.
15506             
15507             for (var i =0; i < Date.dayNames.length; i++) {
15508                 var d = Date.dayNames[i];
15509                 ret.push({
15510                     tag: 'th',
15511                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15512                     html : d.substring(0,3)
15513                 });
15514                 
15515             }
15516             ret[0].cls += ' fc-first';
15517             ret[6].cls += ' fc-last';
15518             return ret;
15519         };
15520         var cal_cell = function(n) {
15521             return  {
15522                 tag: 'td',
15523                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15524                 cn : [
15525                     {
15526                         cn : [
15527                             {
15528                                 cls: 'fc-day-number',
15529                                 html: 'D'
15530                             },
15531                             {
15532                                 cls: 'fc-day-content',
15533                              
15534                                 cn : [
15535                                      {
15536                                         style: 'position: relative;' // height: 17px;
15537                                     }
15538                                 ]
15539                             }
15540                             
15541                             
15542                         ]
15543                     }
15544                 ]
15545                 
15546             }
15547         };
15548         var cal_rows = function() {
15549             
15550             var ret = [];
15551             for (var r = 0; r < 6; r++) {
15552                 var row= {
15553                     tag : 'tr',
15554                     cls : 'fc-week',
15555                     cn : []
15556                 };
15557                 
15558                 for (var i =0; i < Date.dayNames.length; i++) {
15559                     var d = Date.dayNames[i];
15560                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15561
15562                 }
15563                 row.cn[0].cls+=' fc-first';
15564                 row.cn[0].cn[0].style = 'min-height:90px';
15565                 row.cn[6].cls+=' fc-last';
15566                 ret.push(row);
15567                 
15568             }
15569             ret[0].cls += ' fc-first';
15570             ret[4].cls += ' fc-prev-last';
15571             ret[5].cls += ' fc-last';
15572             return ret;
15573             
15574         };
15575         
15576         var cal_table = {
15577             tag: 'table',
15578             cls: 'fc-border-separate',
15579             style : 'width:100%',
15580             cellspacing  : 0,
15581             cn : [
15582                 { 
15583                     tag: 'thead',
15584                     cn : [
15585                         { 
15586                             tag: 'tr',
15587                             cls : 'fc-first fc-last',
15588                             cn : cal_heads()
15589                         }
15590                     ]
15591                 },
15592                 { 
15593                     tag: 'tbody',
15594                     cn : cal_rows()
15595                 }
15596                   
15597             ]
15598         };
15599          
15600          var cfg = {
15601             cls : 'fc fc-ltr',
15602             cn : [
15603                 header,
15604                 {
15605                     cls : 'fc-content',
15606                     style : "position: relative;",
15607                     cn : [
15608                         {
15609                             cls : 'fc-view fc-view-month fc-grid',
15610                             style : 'position: relative',
15611                             unselectable : 'on',
15612                             cn : [
15613                                 {
15614                                     cls : 'fc-event-container',
15615                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15616                                 },
15617                                 cal_table
15618                             ]
15619                         }
15620                     ]
15621     
15622                 }
15623            ] 
15624             
15625         };
15626         
15627          
15628         
15629         return cfg;
15630     },
15631     
15632     
15633     initEvents : function()
15634     {
15635         if(!this.store){
15636             throw "can not find store for calendar";
15637         }
15638         
15639         var mark = {
15640             tag: "div",
15641             cls:"x-dlg-mask",
15642             style: "text-align:center",
15643             cn: [
15644                 {
15645                     tag: "div",
15646                     style: "background-color:white;width:50%;margin:250 auto",
15647                     cn: [
15648                         {
15649                             tag: "img",
15650                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15651                         },
15652                         {
15653                             tag: "span",
15654                             html: "Loading"
15655                         }
15656                         
15657                     ]
15658                 }
15659             ]
15660         };
15661         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15662         
15663         var size = this.el.select('.fc-content', true).first().getSize();
15664         this.maskEl.setSize(size.width, size.height);
15665         this.maskEl.enableDisplayMode("block");
15666         if(!this.loadMask){
15667             this.maskEl.hide();
15668         }
15669         
15670         this.store = Roo.factory(this.store, Roo.data);
15671         this.store.on('load', this.onLoad, this);
15672         this.store.on('beforeload', this.onBeforeLoad, this);
15673         
15674         this.resize();
15675         
15676         this.cells = this.el.select('.fc-day',true);
15677         //Roo.log(this.cells);
15678         this.textNodes = this.el.query('.fc-day-number');
15679         this.cells.addClassOnOver('fc-state-hover');
15680         
15681         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15682         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15683         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15684         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15685         
15686         this.on('monthchange', this.onMonthChange, this);
15687         
15688         this.update(new Date().clearTime());
15689     },
15690     
15691     resize : function() {
15692         var sz  = this.el.getSize();
15693         
15694         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15695         this.el.select('.fc-day-content div',true).setHeight(34);
15696     },
15697     
15698     
15699     // private
15700     showPrevMonth : function(e){
15701         this.update(this.activeDate.add("mo", -1));
15702     },
15703     showToday : function(e){
15704         this.update(new Date().clearTime());
15705     },
15706     // private
15707     showNextMonth : function(e){
15708         this.update(this.activeDate.add("mo", 1));
15709     },
15710
15711     // private
15712     showPrevYear : function(){
15713         this.update(this.activeDate.add("y", -1));
15714     },
15715
15716     // private
15717     showNextYear : function(){
15718         this.update(this.activeDate.add("y", 1));
15719     },
15720
15721     
15722    // private
15723     update : function(date)
15724     {
15725         var vd = this.activeDate;
15726         this.activeDate = date;
15727 //        if(vd && this.el){
15728 //            var t = date.getTime();
15729 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15730 //                Roo.log('using add remove');
15731 //                
15732 //                this.fireEvent('monthchange', this, date);
15733 //                
15734 //                this.cells.removeClass("fc-state-highlight");
15735 //                this.cells.each(function(c){
15736 //                   if(c.dateValue == t){
15737 //                       c.addClass("fc-state-highlight");
15738 //                       setTimeout(function(){
15739 //                            try{c.dom.firstChild.focus();}catch(e){}
15740 //                       }, 50);
15741 //                       return false;
15742 //                   }
15743 //                   return true;
15744 //                });
15745 //                return;
15746 //            }
15747 //        }
15748         
15749         var days = date.getDaysInMonth();
15750         
15751         var firstOfMonth = date.getFirstDateOfMonth();
15752         var startingPos = firstOfMonth.getDay()-this.startDay;
15753         
15754         if(startingPos < this.startDay){
15755             startingPos += 7;
15756         }
15757         
15758         var pm = date.add(Date.MONTH, -1);
15759         var prevStart = pm.getDaysInMonth()-startingPos;
15760 //        
15761         this.cells = this.el.select('.fc-day',true);
15762         this.textNodes = this.el.query('.fc-day-number');
15763         this.cells.addClassOnOver('fc-state-hover');
15764         
15765         var cells = this.cells.elements;
15766         var textEls = this.textNodes;
15767         
15768         Roo.each(cells, function(cell){
15769             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15770         });
15771         
15772         days += startingPos;
15773
15774         // convert everything to numbers so it's fast
15775         var day = 86400000;
15776         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15777         //Roo.log(d);
15778         //Roo.log(pm);
15779         //Roo.log(prevStart);
15780         
15781         var today = new Date().clearTime().getTime();
15782         var sel = date.clearTime().getTime();
15783         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15784         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15785         var ddMatch = this.disabledDatesRE;
15786         var ddText = this.disabledDatesText;
15787         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15788         var ddaysText = this.disabledDaysText;
15789         var format = this.format;
15790         
15791         var setCellClass = function(cal, cell){
15792             cell.row = 0;
15793             cell.events = [];
15794             cell.more = [];
15795             //Roo.log('set Cell Class');
15796             cell.title = "";
15797             var t = d.getTime();
15798             
15799             //Roo.log(d);
15800             
15801             cell.dateValue = t;
15802             if(t == today){
15803                 cell.className += " fc-today";
15804                 cell.className += " fc-state-highlight";
15805                 cell.title = cal.todayText;
15806             }
15807             if(t == sel){
15808                 // disable highlight in other month..
15809                 //cell.className += " fc-state-highlight";
15810                 
15811             }
15812             // disabling
15813             if(t < min) {
15814                 cell.className = " fc-state-disabled";
15815                 cell.title = cal.minText;
15816                 return;
15817             }
15818             if(t > max) {
15819                 cell.className = " fc-state-disabled";
15820                 cell.title = cal.maxText;
15821                 return;
15822             }
15823             if(ddays){
15824                 if(ddays.indexOf(d.getDay()) != -1){
15825                     cell.title = ddaysText;
15826                     cell.className = " fc-state-disabled";
15827                 }
15828             }
15829             if(ddMatch && format){
15830                 var fvalue = d.dateFormat(format);
15831                 if(ddMatch.test(fvalue)){
15832                     cell.title = ddText.replace("%0", fvalue);
15833                     cell.className = " fc-state-disabled";
15834                 }
15835             }
15836             
15837             if (!cell.initialClassName) {
15838                 cell.initialClassName = cell.dom.className;
15839             }
15840             
15841             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15842         };
15843
15844         var i = 0;
15845         
15846         for(; i < startingPos; i++) {
15847             textEls[i].innerHTML = (++prevStart);
15848             d.setDate(d.getDate()+1);
15849             
15850             cells[i].className = "fc-past fc-other-month";
15851             setCellClass(this, cells[i]);
15852         }
15853         
15854         var intDay = 0;
15855         
15856         for(; i < days; i++){
15857             intDay = i - startingPos + 1;
15858             textEls[i].innerHTML = (intDay);
15859             d.setDate(d.getDate()+1);
15860             
15861             cells[i].className = ''; // "x-date-active";
15862             setCellClass(this, cells[i]);
15863         }
15864         var extraDays = 0;
15865         
15866         for(; i < 42; i++) {
15867             textEls[i].innerHTML = (++extraDays);
15868             d.setDate(d.getDate()+1);
15869             
15870             cells[i].className = "fc-future fc-other-month";
15871             setCellClass(this, cells[i]);
15872         }
15873         
15874         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15875         
15876         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15877         
15878         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15879         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15880         
15881         if(totalRows != 6){
15882             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15883             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15884         }
15885         
15886         this.fireEvent('monthchange', this, date);
15887         
15888         
15889         /*
15890         if(!this.internalRender){
15891             var main = this.el.dom.firstChild;
15892             var w = main.offsetWidth;
15893             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15894             Roo.fly(main).setWidth(w);
15895             this.internalRender = true;
15896             // opera does not respect the auto grow header center column
15897             // then, after it gets a width opera refuses to recalculate
15898             // without a second pass
15899             if(Roo.isOpera && !this.secondPass){
15900                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15901                 this.secondPass = true;
15902                 this.update.defer(10, this, [date]);
15903             }
15904         }
15905         */
15906         
15907     },
15908     
15909     findCell : function(dt) {
15910         dt = dt.clearTime().getTime();
15911         var ret = false;
15912         this.cells.each(function(c){
15913             //Roo.log("check " +c.dateValue + '?=' + dt);
15914             if(c.dateValue == dt){
15915                 ret = c;
15916                 return false;
15917             }
15918             return true;
15919         });
15920         
15921         return ret;
15922     },
15923     
15924     findCells : function(ev) {
15925         var s = ev.start.clone().clearTime().getTime();
15926        // Roo.log(s);
15927         var e= ev.end.clone().clearTime().getTime();
15928        // Roo.log(e);
15929         var ret = [];
15930         this.cells.each(function(c){
15931              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15932             
15933             if(c.dateValue > e){
15934                 return ;
15935             }
15936             if(c.dateValue < s){
15937                 return ;
15938             }
15939             ret.push(c);
15940         });
15941         
15942         return ret;    
15943     },
15944     
15945 //    findBestRow: function(cells)
15946 //    {
15947 //        var ret = 0;
15948 //        
15949 //        for (var i =0 ; i < cells.length;i++) {
15950 //            ret  = Math.max(cells[i].rows || 0,ret);
15951 //        }
15952 //        return ret;
15953 //        
15954 //    },
15955     
15956     
15957     addItem : function(ev)
15958     {
15959         // look for vertical location slot in
15960         var cells = this.findCells(ev);
15961         
15962 //        ev.row = this.findBestRow(cells);
15963         
15964         // work out the location.
15965         
15966         var crow = false;
15967         var rows = [];
15968         for(var i =0; i < cells.length; i++) {
15969             
15970             cells[i].row = cells[0].row;
15971             
15972             if(i == 0){
15973                 cells[i].row = cells[i].row + 1;
15974             }
15975             
15976             if (!crow) {
15977                 crow = {
15978                     start : cells[i],
15979                     end :  cells[i]
15980                 };
15981                 continue;
15982             }
15983             if (crow.start.getY() == cells[i].getY()) {
15984                 // on same row.
15985                 crow.end = cells[i];
15986                 continue;
15987             }
15988             // different row.
15989             rows.push(crow);
15990             crow = {
15991                 start: cells[i],
15992                 end : cells[i]
15993             };
15994             
15995         }
15996         
15997         rows.push(crow);
15998         ev.els = [];
15999         ev.rows = rows;
16000         ev.cells = cells;
16001         
16002         cells[0].events.push(ev);
16003         
16004         this.calevents.push(ev);
16005     },
16006     
16007     clearEvents: function() {
16008         
16009         if(!this.calevents){
16010             return;
16011         }
16012         
16013         Roo.each(this.cells.elements, function(c){
16014             c.row = 0;
16015             c.events = [];
16016             c.more = [];
16017         });
16018         
16019         Roo.each(this.calevents, function(e) {
16020             Roo.each(e.els, function(el) {
16021                 el.un('mouseenter' ,this.onEventEnter, this);
16022                 el.un('mouseleave' ,this.onEventLeave, this);
16023                 el.remove();
16024             },this);
16025         },this);
16026         
16027         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16028             e.remove();
16029         });
16030         
16031     },
16032     
16033     renderEvents: function()
16034     {   
16035         var _this = this;
16036         
16037         this.cells.each(function(c) {
16038             
16039             if(c.row < 5){
16040                 return;
16041             }
16042             
16043             var ev = c.events;
16044             
16045             var r = 4;
16046             if(c.row != c.events.length){
16047                 r = 4 - (4 - (c.row - c.events.length));
16048             }
16049             
16050             c.events = ev.slice(0, r);
16051             c.more = ev.slice(r);
16052             
16053             if(c.more.length && c.more.length == 1){
16054                 c.events.push(c.more.pop());
16055             }
16056             
16057             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16058             
16059         });
16060             
16061         this.cells.each(function(c) {
16062             
16063             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16064             
16065             
16066             for (var e = 0; e < c.events.length; e++){
16067                 var ev = c.events[e];
16068                 var rows = ev.rows;
16069                 
16070                 for(var i = 0; i < rows.length; i++) {
16071                 
16072                     // how many rows should it span..
16073
16074                     var  cfg = {
16075                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16076                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16077
16078                         unselectable : "on",
16079                         cn : [
16080                             {
16081                                 cls: 'fc-event-inner',
16082                                 cn : [
16083     //                                {
16084     //                                  tag:'span',
16085     //                                  cls: 'fc-event-time',
16086     //                                  html : cells.length > 1 ? '' : ev.time
16087     //                                },
16088                                     {
16089                                       tag:'span',
16090                                       cls: 'fc-event-title',
16091                                       html : String.format('{0}', ev.title)
16092                                     }
16093
16094
16095                                 ]
16096                             },
16097                             {
16098                                 cls: 'ui-resizable-handle ui-resizable-e',
16099                                 html : '&nbsp;&nbsp;&nbsp'
16100                             }
16101
16102                         ]
16103                     };
16104
16105                     if (i == 0) {
16106                         cfg.cls += ' fc-event-start';
16107                     }
16108                     if ((i+1) == rows.length) {
16109                         cfg.cls += ' fc-event-end';
16110                     }
16111
16112                     var ctr = _this.el.select('.fc-event-container',true).first();
16113                     var cg = ctr.createChild(cfg);
16114
16115                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16116                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16117
16118                     var r = (c.more.length) ? 1 : 0;
16119                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16120                     cg.setWidth(ebox.right - sbox.x -2);
16121
16122                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16123                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16124                     cg.on('click', _this.onEventClick, _this, ev);
16125
16126                     ev.els.push(cg);
16127                     
16128                 }
16129                 
16130             }
16131             
16132             
16133             if(c.more.length){
16134                 var  cfg = {
16135                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16136                     style : 'position: absolute',
16137                     unselectable : "on",
16138                     cn : [
16139                         {
16140                             cls: 'fc-event-inner',
16141                             cn : [
16142                                 {
16143                                   tag:'span',
16144                                   cls: 'fc-event-title',
16145                                   html : 'More'
16146                                 }
16147
16148
16149                             ]
16150                         },
16151                         {
16152                             cls: 'ui-resizable-handle ui-resizable-e',
16153                             html : '&nbsp;&nbsp;&nbsp'
16154                         }
16155
16156                     ]
16157                 };
16158
16159                 var ctr = _this.el.select('.fc-event-container',true).first();
16160                 var cg = ctr.createChild(cfg);
16161
16162                 var sbox = c.select('.fc-day-content',true).first().getBox();
16163                 var ebox = c.select('.fc-day-content',true).first().getBox();
16164                 //Roo.log(cg);
16165                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16166                 cg.setWidth(ebox.right - sbox.x -2);
16167
16168                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16169                 
16170             }
16171             
16172         });
16173         
16174         
16175         
16176     },
16177     
16178     onEventEnter: function (e, el,event,d) {
16179         this.fireEvent('evententer', this, el, event);
16180     },
16181     
16182     onEventLeave: function (e, el,event,d) {
16183         this.fireEvent('eventleave', this, el, event);
16184     },
16185     
16186     onEventClick: function (e, el,event,d) {
16187         this.fireEvent('eventclick', this, el, event);
16188     },
16189     
16190     onMonthChange: function () {
16191         this.store.load();
16192     },
16193     
16194     onMoreEventClick: function(e, el, more)
16195     {
16196         var _this = this;
16197         
16198         this.calpopover.placement = 'right';
16199         this.calpopover.setTitle('More');
16200         
16201         this.calpopover.setContent('');
16202         
16203         var ctr = this.calpopover.el.select('.popover-content', true).first();
16204         
16205         Roo.each(more, function(m){
16206             var cfg = {
16207                 cls : 'fc-event-hori fc-event-draggable',
16208                 html : m.title
16209             };
16210             var cg = ctr.createChild(cfg);
16211             
16212             cg.on('click', _this.onEventClick, _this, m);
16213         });
16214         
16215         this.calpopover.show(el);
16216         
16217         
16218     },
16219     
16220     onLoad: function () 
16221     {   
16222         this.calevents = [];
16223         var cal = this;
16224         
16225         if(this.store.getCount() > 0){
16226             this.store.data.each(function(d){
16227                cal.addItem({
16228                     id : d.data.id,
16229                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16230                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16231                     time : d.data.start_time,
16232                     title : d.data.title,
16233                     description : d.data.description,
16234                     venue : d.data.venue
16235                 });
16236             });
16237         }
16238         
16239         this.renderEvents();
16240         
16241         if(this.calevents.length && this.loadMask){
16242             this.maskEl.hide();
16243         }
16244     },
16245     
16246     onBeforeLoad: function()
16247     {
16248         this.clearEvents();
16249         if(this.loadMask){
16250             this.maskEl.show();
16251         }
16252     }
16253 });
16254
16255  
16256  /*
16257  * - LGPL
16258  *
16259  * element
16260  * 
16261  */
16262
16263 /**
16264  * @class Roo.bootstrap.Popover
16265  * @extends Roo.bootstrap.Component
16266  * Bootstrap Popover class
16267  * @cfg {String} html contents of the popover   (or false to use children..)
16268  * @cfg {String} title of popover (or false to hide)
16269  * @cfg {String} placement how it is placed
16270  * @cfg {String} trigger click || hover (or false to trigger manually)
16271  * @cfg {String} over what (parent or false to trigger manually.)
16272  * @cfg {Number} delay - delay before showing
16273  
16274  * @constructor
16275  * Create a new Popover
16276  * @param {Object} config The config object
16277  */
16278
16279 Roo.bootstrap.Popover = function(config){
16280     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16281     
16282     this.addEvents({
16283         // raw events
16284          /**
16285          * @event show
16286          * After the popover show
16287          * 
16288          * @param {Roo.bootstrap.Popover} this
16289          */
16290         "show" : true,
16291         /**
16292          * @event hide
16293          * After the popover hide
16294          * 
16295          * @param {Roo.bootstrap.Popover} this
16296          */
16297         "hide" : true
16298     });
16299 };
16300
16301 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16302     
16303     title: 'Fill in a title',
16304     html: false,
16305     
16306     placement : 'right',
16307     trigger : 'hover', // hover
16308     
16309     delay : 0,
16310     
16311     over: 'parent',
16312     
16313     can_build_overlaid : false,
16314     
16315     getChildContainer : function()
16316     {
16317         return this.el.select('.popover-content',true).first();
16318     },
16319     
16320     getAutoCreate : function(){
16321          
16322         var cfg = {
16323            cls : 'popover roo-dynamic',
16324            style: 'display:block',
16325            cn : [
16326                 {
16327                     cls : 'arrow'
16328                 },
16329                 {
16330                     cls : 'popover-inner',
16331                     cn : [
16332                         {
16333                             tag: 'h3',
16334                             cls: 'popover-title',
16335                             html : this.title
16336                         },
16337                         {
16338                             cls : 'popover-content',
16339                             html : this.html
16340                         }
16341                     ]
16342                     
16343                 }
16344            ]
16345         };
16346         
16347         return cfg;
16348     },
16349     setTitle: function(str)
16350     {
16351         this.title = str;
16352         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16353     },
16354     setContent: function(str)
16355     {
16356         this.html = str;
16357         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16358     },
16359     // as it get's added to the bottom of the page.
16360     onRender : function(ct, position)
16361     {
16362         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16363         if(!this.el){
16364             var cfg = Roo.apply({},  this.getAutoCreate());
16365             cfg.id = Roo.id();
16366             
16367             if (this.cls) {
16368                 cfg.cls += ' ' + this.cls;
16369             }
16370             if (this.style) {
16371                 cfg.style = this.style;
16372             }
16373             //Roo.log("adding to ");
16374             this.el = Roo.get(document.body).createChild(cfg, position);
16375 //            Roo.log(this.el);
16376         }
16377         this.initEvents();
16378     },
16379     
16380     initEvents : function()
16381     {
16382         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16383         this.el.enableDisplayMode('block');
16384         this.el.hide();
16385         if (this.over === false) {
16386             return; 
16387         }
16388         if (this.triggers === false) {
16389             return;
16390         }
16391         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16392         var triggers = this.trigger ? this.trigger.split(' ') : [];
16393         Roo.each(triggers, function(trigger) {
16394         
16395             if (trigger == 'click') {
16396                 on_el.on('click', this.toggle, this);
16397             } else if (trigger != 'manual') {
16398                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16399                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16400       
16401                 on_el.on(eventIn  ,this.enter, this);
16402                 on_el.on(eventOut, this.leave, this);
16403             }
16404         }, this);
16405         
16406     },
16407     
16408     
16409     // private
16410     timeout : null,
16411     hoverState : null,
16412     
16413     toggle : function () {
16414         this.hoverState == 'in' ? this.leave() : this.enter();
16415     },
16416     
16417     enter : function () {
16418         
16419         clearTimeout(this.timeout);
16420     
16421         this.hoverState = 'in';
16422     
16423         if (!this.delay || !this.delay.show) {
16424             this.show();
16425             return;
16426         }
16427         var _t = this;
16428         this.timeout = setTimeout(function () {
16429             if (_t.hoverState == 'in') {
16430                 _t.show();
16431             }
16432         }, this.delay.show)
16433     },
16434     
16435     leave : function() {
16436         clearTimeout(this.timeout);
16437     
16438         this.hoverState = 'out';
16439     
16440         if (!this.delay || !this.delay.hide) {
16441             this.hide();
16442             return;
16443         }
16444         var _t = this;
16445         this.timeout = setTimeout(function () {
16446             if (_t.hoverState == 'out') {
16447                 _t.hide();
16448             }
16449         }, this.delay.hide)
16450     },
16451     
16452     show : function (on_el)
16453     {
16454         if (!on_el) {
16455             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16456         }
16457         
16458         // set content.
16459         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16460         if (this.html !== false) {
16461             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16462         }
16463         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16464         if (!this.title.length) {
16465             this.el.select('.popover-title',true).hide();
16466         }
16467         
16468         var placement = typeof this.placement == 'function' ?
16469             this.placement.call(this, this.el, on_el) :
16470             this.placement;
16471             
16472         var autoToken = /\s?auto?\s?/i;
16473         var autoPlace = autoToken.test(placement);
16474         if (autoPlace) {
16475             placement = placement.replace(autoToken, '') || 'top';
16476         }
16477         
16478         //this.el.detach()
16479         //this.el.setXY([0,0]);
16480         this.el.show();
16481         this.el.dom.style.display='block';
16482         this.el.addClass(placement);
16483         
16484         //this.el.appendTo(on_el);
16485         
16486         var p = this.getPosition();
16487         var box = this.el.getBox();
16488         
16489         if (autoPlace) {
16490             // fixme..
16491         }
16492         var align = Roo.bootstrap.Popover.alignment[placement];
16493         this.el.alignTo(on_el, align[0],align[1]);
16494         //var arrow = this.el.select('.arrow',true).first();
16495         //arrow.set(align[2], 
16496         
16497         this.el.addClass('in');
16498         
16499         
16500         if (this.el.hasClass('fade')) {
16501             // fade it?
16502         }
16503         
16504         this.hoverState = 'in';
16505         
16506         this.fireEvent('show', this);
16507         
16508     },
16509     hide : function()
16510     {
16511         this.el.setXY([0,0]);
16512         this.el.removeClass('in');
16513         this.el.hide();
16514         this.hoverState = null;
16515         
16516         this.fireEvent('hide', this);
16517     }
16518     
16519 });
16520
16521 Roo.bootstrap.Popover.alignment = {
16522     'left' : ['r-l', [-10,0], 'right'],
16523     'right' : ['l-r', [10,0], 'left'],
16524     'bottom' : ['t-b', [0,10], 'top'],
16525     'top' : [ 'b-t', [0,-10], 'bottom']
16526 };
16527
16528  /*
16529  * - LGPL
16530  *
16531  * Progress
16532  * 
16533  */
16534
16535 /**
16536  * @class Roo.bootstrap.Progress
16537  * @extends Roo.bootstrap.Component
16538  * Bootstrap Progress class
16539  * @cfg {Boolean} striped striped of the progress bar
16540  * @cfg {Boolean} active animated of the progress bar
16541  * 
16542  * 
16543  * @constructor
16544  * Create a new Progress
16545  * @param {Object} config The config object
16546  */
16547
16548 Roo.bootstrap.Progress = function(config){
16549     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16550 };
16551
16552 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16553     
16554     striped : false,
16555     active: false,
16556     
16557     getAutoCreate : function(){
16558         var cfg = {
16559             tag: 'div',
16560             cls: 'progress'
16561         };
16562         
16563         
16564         if(this.striped){
16565             cfg.cls += ' progress-striped';
16566         }
16567       
16568         if(this.active){
16569             cfg.cls += ' active';
16570         }
16571         
16572         
16573         return cfg;
16574     }
16575    
16576 });
16577
16578  
16579
16580  /*
16581  * - LGPL
16582  *
16583  * ProgressBar
16584  * 
16585  */
16586
16587 /**
16588  * @class Roo.bootstrap.ProgressBar
16589  * @extends Roo.bootstrap.Component
16590  * Bootstrap ProgressBar class
16591  * @cfg {Number} aria_valuenow aria-value now
16592  * @cfg {Number} aria_valuemin aria-value min
16593  * @cfg {Number} aria_valuemax aria-value max
16594  * @cfg {String} label label for the progress bar
16595  * @cfg {String} panel (success | info | warning | danger )
16596  * @cfg {String} role role of the progress bar
16597  * @cfg {String} sr_only text
16598  * 
16599  * 
16600  * @constructor
16601  * Create a new ProgressBar
16602  * @param {Object} config The config object
16603  */
16604
16605 Roo.bootstrap.ProgressBar = function(config){
16606     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16607 };
16608
16609 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16610     
16611     aria_valuenow : 0,
16612     aria_valuemin : 0,
16613     aria_valuemax : 100,
16614     label : false,
16615     panel : false,
16616     role : false,
16617     sr_only: false,
16618     
16619     getAutoCreate : function()
16620     {
16621         
16622         var cfg = {
16623             tag: 'div',
16624             cls: 'progress-bar',
16625             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16626         };
16627         
16628         if(this.sr_only){
16629             cfg.cn = {
16630                 tag: 'span',
16631                 cls: 'sr-only',
16632                 html: this.sr_only
16633             }
16634         }
16635         
16636         if(this.role){
16637             cfg.role = this.role;
16638         }
16639         
16640         if(this.aria_valuenow){
16641             cfg['aria-valuenow'] = this.aria_valuenow;
16642         }
16643         
16644         if(this.aria_valuemin){
16645             cfg['aria-valuemin'] = this.aria_valuemin;
16646         }
16647         
16648         if(this.aria_valuemax){
16649             cfg['aria-valuemax'] = this.aria_valuemax;
16650         }
16651         
16652         if(this.label && !this.sr_only){
16653             cfg.html = this.label;
16654         }
16655         
16656         if(this.panel){
16657             cfg.cls += ' progress-bar-' + this.panel;
16658         }
16659         
16660         return cfg;
16661     },
16662     
16663     update : function(aria_valuenow)
16664     {
16665         this.aria_valuenow = aria_valuenow;
16666         
16667         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16668     }
16669    
16670 });
16671
16672  
16673
16674  /*
16675  * - LGPL
16676  *
16677  * column
16678  * 
16679  */
16680
16681 /**
16682  * @class Roo.bootstrap.TabGroup
16683  * @extends Roo.bootstrap.Column
16684  * Bootstrap Column class
16685  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16686  * @cfg {Boolean} carousel true to make the group behave like a carousel
16687  * @cfg {Boolean} bullets show bullets for the panels
16688  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16689  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16690  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16691  * @cfg {Boolean} showarrow (true|false) show arrow default true
16692  * 
16693  * @constructor
16694  * Create a new TabGroup
16695  * @param {Object} config The config object
16696  */
16697
16698 Roo.bootstrap.TabGroup = function(config){
16699     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16700     if (!this.navId) {
16701         this.navId = Roo.id();
16702     }
16703     this.tabs = [];
16704     Roo.bootstrap.TabGroup.register(this);
16705     
16706 };
16707
16708 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16709     
16710     carousel : false,
16711     transition : false,
16712     bullets : 0,
16713     timer : 0,
16714     autoslide : false,
16715     slideFn : false,
16716     slideOnTouch : false,
16717     showarrow : true,
16718     
16719     getAutoCreate : function()
16720     {
16721         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16722         
16723         cfg.cls += ' tab-content';
16724         
16725         if (this.carousel) {
16726             cfg.cls += ' carousel slide';
16727             
16728             cfg.cn = [{
16729                cls : 'carousel-inner',
16730                cn : []
16731             }];
16732         
16733             if(this.bullets  && !Roo.isTouch){
16734                 
16735                 var bullets = {
16736                     cls : 'carousel-bullets',
16737                     cn : []
16738                 };
16739                
16740                 if(this.bullets_cls){
16741                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16742                 }
16743                 
16744                 bullets.cn.push({
16745                     cls : 'clear'
16746                 });
16747                 
16748                 cfg.cn[0].cn.push(bullets);
16749             }
16750             
16751             if(this.showarrow){
16752                 cfg.cn[0].cn.push({
16753                     tag : 'div',
16754                     class : 'carousel-arrow',
16755                     cn : [
16756                         {
16757                             tag : 'div',
16758                             class : 'carousel-prev',
16759                             cn : [
16760                                 {
16761                                     tag : 'i',
16762                                     class : 'fa fa-chevron-left'
16763                                 }
16764                             ]
16765                         },
16766                         {
16767                             tag : 'div',
16768                             class : 'carousel-next',
16769                             cn : [
16770                                 {
16771                                     tag : 'i',
16772                                     class : 'fa fa-chevron-right'
16773                                 }
16774                             ]
16775                         }
16776                     ]
16777                 });
16778             }
16779             
16780         }
16781         
16782         return cfg;
16783     },
16784     
16785     initEvents:  function()
16786     {
16787         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16788             this.el.on("touchstart", this.onTouchStart, this);
16789         }
16790         
16791         if(this.autoslide){
16792             var _this = this;
16793             
16794             this.slideFn = window.setInterval(function() {
16795                 _this.showPanelNext();
16796             }, this.timer);
16797         }
16798         
16799         if(this.showarrow){
16800             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16801             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16802         }
16803         
16804         
16805     },
16806     
16807     onTouchStart : function(e, el, o)
16808     {
16809         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16810             return;
16811         }
16812         
16813         this.showPanelNext();
16814     },
16815     
16816     getChildContainer : function()
16817     {
16818         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16819     },
16820     
16821     /**
16822     * register a Navigation item
16823     * @param {Roo.bootstrap.NavItem} the navitem to add
16824     */
16825     register : function(item)
16826     {
16827         this.tabs.push( item);
16828         item.navId = this.navId; // not really needed..
16829         this.addBullet();
16830     
16831     },
16832     
16833     getActivePanel : function()
16834     {
16835         var r = false;
16836         Roo.each(this.tabs, function(t) {
16837             if (t.active) {
16838                 r = t;
16839                 return false;
16840             }
16841             return null;
16842         });
16843         return r;
16844         
16845     },
16846     getPanelByName : function(n)
16847     {
16848         var r = false;
16849         Roo.each(this.tabs, function(t) {
16850             if (t.tabId == n) {
16851                 r = t;
16852                 return false;
16853             }
16854             return null;
16855         });
16856         return r;
16857     },
16858     indexOfPanel : function(p)
16859     {
16860         var r = false;
16861         Roo.each(this.tabs, function(t,i) {
16862             if (t.tabId == p.tabId) {
16863                 r = i;
16864                 return false;
16865             }
16866             return null;
16867         });
16868         return r;
16869     },
16870     /**
16871      * show a specific panel
16872      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16873      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16874      */
16875     showPanel : function (pan)
16876     {
16877         if(this.transition || typeof(pan) == 'undefined'){
16878             Roo.log("waiting for the transitionend");
16879             return;
16880         }
16881         
16882         if (typeof(pan) == 'number') {
16883             pan = this.tabs[pan];
16884         }
16885         
16886         if (typeof(pan) == 'string') {
16887             pan = this.getPanelByName(pan);
16888         }
16889         
16890         var cur = this.getActivePanel();
16891         
16892         if(!pan || !cur){
16893             Roo.log('pan or acitve pan is undefined');
16894             return false;
16895         }
16896         
16897         if (pan.tabId == this.getActivePanel().tabId) {
16898             return true;
16899         }
16900         
16901         if (false === cur.fireEvent('beforedeactivate')) {
16902             return false;
16903         }
16904         
16905         if(this.bullets > 0 && !Roo.isTouch){
16906             this.setActiveBullet(this.indexOfPanel(pan));
16907         }
16908         
16909         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16910             
16911             this.transition = true;
16912             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16913             var lr = dir == 'next' ? 'left' : 'right';
16914             pan.el.addClass(dir); // or prev
16915             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16916             cur.el.addClass(lr); // or right
16917             pan.el.addClass(lr);
16918             
16919             var _this = this;
16920             cur.el.on('transitionend', function() {
16921                 Roo.log("trans end?");
16922                 
16923                 pan.el.removeClass([lr,dir]);
16924                 pan.setActive(true);
16925                 
16926                 cur.el.removeClass([lr]);
16927                 cur.setActive(false);
16928                 
16929                 _this.transition = false;
16930                 
16931             }, this, { single:  true } );
16932             
16933             return true;
16934         }
16935         
16936         cur.setActive(false);
16937         pan.setActive(true);
16938         
16939         return true;
16940         
16941     },
16942     showPanelNext : function()
16943     {
16944         var i = this.indexOfPanel(this.getActivePanel());
16945         
16946         if (i >= this.tabs.length - 1 && !this.autoslide) {
16947             return;
16948         }
16949         
16950         if (i >= this.tabs.length - 1 && this.autoslide) {
16951             i = -1;
16952         }
16953         
16954         this.showPanel(this.tabs[i+1]);
16955     },
16956     
16957     showPanelPrev : function()
16958     {
16959         var i = this.indexOfPanel(this.getActivePanel());
16960         
16961         if (i  < 1 && !this.autoslide) {
16962             return;
16963         }
16964         
16965         if (i < 1 && this.autoslide) {
16966             i = this.tabs.length;
16967         }
16968         
16969         this.showPanel(this.tabs[i-1]);
16970     },
16971     
16972     
16973     addBullet: function()
16974     {
16975         if(!this.bullets || Roo.isTouch){
16976             return;
16977         }
16978         var ctr = this.el.select('.carousel-bullets',true).first();
16979         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16980         var bullet = ctr.createChild({
16981             cls : 'bullet bullet-' + i
16982         },ctr.dom.lastChild);
16983         
16984         
16985         var _this = this;
16986         
16987         bullet.on('click', (function(e, el, o, ii, t){
16988
16989             e.preventDefault();
16990
16991             this.showPanel(ii);
16992
16993             if(this.autoslide && this.slideFn){
16994                 clearInterval(this.slideFn);
16995                 this.slideFn = window.setInterval(function() {
16996                     _this.showPanelNext();
16997                 }, this.timer);
16998             }
16999
17000         }).createDelegate(this, [i, bullet], true));
17001                 
17002         
17003     },
17004      
17005     setActiveBullet : function(i)
17006     {
17007         if(Roo.isTouch){
17008             return;
17009         }
17010         
17011         Roo.each(this.el.select('.bullet', true).elements, function(el){
17012             el.removeClass('selected');
17013         });
17014
17015         var bullet = this.el.select('.bullet-' + i, true).first();
17016         
17017         if(!bullet){
17018             return;
17019         }
17020         
17021         bullet.addClass('selected');
17022     }
17023     
17024     
17025   
17026 });
17027
17028  
17029
17030  
17031  
17032 Roo.apply(Roo.bootstrap.TabGroup, {
17033     
17034     groups: {},
17035      /**
17036     * register a Navigation Group
17037     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17038     */
17039     register : function(navgrp)
17040     {
17041         this.groups[navgrp.navId] = navgrp;
17042         
17043     },
17044     /**
17045     * fetch a Navigation Group based on the navigation ID
17046     * if one does not exist , it will get created.
17047     * @param {string} the navgroup to add
17048     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17049     */
17050     get: function(navId) {
17051         if (typeof(this.groups[navId]) == 'undefined') {
17052             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17053         }
17054         return this.groups[navId] ;
17055     }
17056     
17057     
17058     
17059 });
17060
17061  /*
17062  * - LGPL
17063  *
17064  * TabPanel
17065  * 
17066  */
17067
17068 /**
17069  * @class Roo.bootstrap.TabPanel
17070  * @extends Roo.bootstrap.Component
17071  * Bootstrap TabPanel class
17072  * @cfg {Boolean} active panel active
17073  * @cfg {String} html panel content
17074  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17075  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17076  * @cfg {String} href click to link..
17077  * 
17078  * 
17079  * @constructor
17080  * Create a new TabPanel
17081  * @param {Object} config The config object
17082  */
17083
17084 Roo.bootstrap.TabPanel = function(config){
17085     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17086     this.addEvents({
17087         /**
17088              * @event changed
17089              * Fires when the active status changes
17090              * @param {Roo.bootstrap.TabPanel} this
17091              * @param {Boolean} state the new state
17092             
17093          */
17094         'changed': true,
17095         /**
17096              * @event beforedeactivate
17097              * Fires before a tab is de-activated - can be used to do validation on a form.
17098              * @param {Roo.bootstrap.TabPanel} this
17099              * @return {Boolean} false if there is an error
17100             
17101          */
17102         'beforedeactivate': true
17103      });
17104     
17105     this.tabId = this.tabId || Roo.id();
17106   
17107 };
17108
17109 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17110     
17111     active: false,
17112     html: false,
17113     tabId: false,
17114     navId : false,
17115     href : '',
17116     
17117     getAutoCreate : function(){
17118         var cfg = {
17119             tag: 'div',
17120             // item is needed for carousel - not sure if it has any effect otherwise
17121             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17122             html: this.html || ''
17123         };
17124         
17125         if(this.active){
17126             cfg.cls += ' active';
17127         }
17128         
17129         if(this.tabId){
17130             cfg.tabId = this.tabId;
17131         }
17132         
17133         
17134         return cfg;
17135     },
17136     
17137     initEvents:  function()
17138     {
17139         var p = this.parent();
17140         this.navId = this.navId || p.navId;
17141         
17142         if (typeof(this.navId) != 'undefined') {
17143             // not really needed.. but just in case.. parent should be a NavGroup.
17144             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17145             
17146             tg.register(this);
17147             
17148             var i = tg.tabs.length - 1;
17149             
17150             if(this.active && tg.bullets > 0 && i < tg.bullets){
17151                 tg.setActiveBullet(i);
17152             }
17153         }
17154         
17155         if(this.href.length){
17156             this.el.on('click', this.onClick, this);
17157         }
17158         
17159     },
17160     
17161     onRender : function(ct, position)
17162     {
17163        // Roo.log("Call onRender: " + this.xtype);
17164         
17165         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17166         
17167         
17168         
17169         
17170         
17171     },
17172     
17173     setActive: function(state)
17174     {
17175         Roo.log("panel - set active " + this.tabId + "=" + state);
17176         
17177         this.active = state;
17178         if (!state) {
17179             this.el.removeClass('active');
17180             
17181         } else  if (!this.el.hasClass('active')) {
17182             this.el.addClass('active');
17183         }
17184         
17185         this.fireEvent('changed', this, state);
17186     },
17187     
17188     onClick: function(e)
17189     {
17190         e.preventDefault();
17191         
17192         window.location.href = this.href;
17193     }
17194     
17195     
17196 });
17197  
17198
17199  
17200
17201  /*
17202  * - LGPL
17203  *
17204  * DateField
17205  * 
17206  */
17207
17208 /**
17209  * @class Roo.bootstrap.DateField
17210  * @extends Roo.bootstrap.Input
17211  * Bootstrap DateField class
17212  * @cfg {Number} weekStart default 0
17213  * @cfg {String} viewMode default empty, (months|years)
17214  * @cfg {String} minViewMode default empty, (months|years)
17215  * @cfg {Number} startDate default -Infinity
17216  * @cfg {Number} endDate default Infinity
17217  * @cfg {Boolean} todayHighlight default false
17218  * @cfg {Boolean} todayBtn default false
17219  * @cfg {Boolean} calendarWeeks default false
17220  * @cfg {Object} daysOfWeekDisabled default empty
17221  * @cfg {Boolean} singleMode default false (true | false)
17222  * 
17223  * @cfg {Boolean} keyboardNavigation default true
17224  * @cfg {String} language default en
17225  * 
17226  * @constructor
17227  * Create a new DateField
17228  * @param {Object} config The config object
17229  */
17230
17231 Roo.bootstrap.DateField = function(config){
17232     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17233      this.addEvents({
17234             /**
17235              * @event show
17236              * Fires when this field show.
17237              * @param {Roo.bootstrap.DateField} this
17238              * @param {Mixed} date The date value
17239              */
17240             show : true,
17241             /**
17242              * @event show
17243              * Fires when this field hide.
17244              * @param {Roo.bootstrap.DateField} this
17245              * @param {Mixed} date The date value
17246              */
17247             hide : true,
17248             /**
17249              * @event select
17250              * Fires when select a date.
17251              * @param {Roo.bootstrap.DateField} this
17252              * @param {Mixed} date The date value
17253              */
17254             select : true,
17255             /**
17256              * @event beforeselect
17257              * Fires when before select a date.
17258              * @param {Roo.bootstrap.DateField} this
17259              * @param {Mixed} date The date value
17260              */
17261             beforeselect : true
17262         });
17263 };
17264
17265 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17266     
17267     /**
17268      * @cfg {String} format
17269      * The default date format string which can be overriden for localization support.  The format must be
17270      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17271      */
17272     format : "m/d/y",
17273     /**
17274      * @cfg {String} altFormats
17275      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17276      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17277      */
17278     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17279     
17280     weekStart : 0,
17281     
17282     viewMode : '',
17283     
17284     minViewMode : '',
17285     
17286     todayHighlight : false,
17287     
17288     todayBtn: false,
17289     
17290     language: 'en',
17291     
17292     keyboardNavigation: true,
17293     
17294     calendarWeeks: false,
17295     
17296     startDate: -Infinity,
17297     
17298     endDate: Infinity,
17299     
17300     daysOfWeekDisabled: [],
17301     
17302     _events: [],
17303     
17304     singleMode : false,
17305     
17306     UTCDate: function()
17307     {
17308         return new Date(Date.UTC.apply(Date, arguments));
17309     },
17310     
17311     UTCToday: function()
17312     {
17313         var today = new Date();
17314         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17315     },
17316     
17317     getDate: function() {
17318             var d = this.getUTCDate();
17319             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17320     },
17321     
17322     getUTCDate: function() {
17323             return this.date;
17324     },
17325     
17326     setDate: function(d) {
17327             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17328     },
17329     
17330     setUTCDate: function(d) {
17331             this.date = d;
17332             this.setValue(this.formatDate(this.date));
17333     },
17334         
17335     onRender: function(ct, position)
17336     {
17337         
17338         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17339         
17340         this.language = this.language || 'en';
17341         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17342         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17343         
17344         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17345         this.format = this.format || 'm/d/y';
17346         this.isInline = false;
17347         this.isInput = true;
17348         this.component = this.el.select('.add-on', true).first() || false;
17349         this.component = (this.component && this.component.length === 0) ? false : this.component;
17350         this.hasInput = this.component && this.inputEl().length;
17351         
17352         if (typeof(this.minViewMode === 'string')) {
17353             switch (this.minViewMode) {
17354                 case 'months':
17355                     this.minViewMode = 1;
17356                     break;
17357                 case 'years':
17358                     this.minViewMode = 2;
17359                     break;
17360                 default:
17361                     this.minViewMode = 0;
17362                     break;
17363             }
17364         }
17365         
17366         if (typeof(this.viewMode === 'string')) {
17367             switch (this.viewMode) {
17368                 case 'months':
17369                     this.viewMode = 1;
17370                     break;
17371                 case 'years':
17372                     this.viewMode = 2;
17373                     break;
17374                 default:
17375                     this.viewMode = 0;
17376                     break;
17377             }
17378         }
17379                 
17380         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17381         
17382 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17383         
17384         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17385         
17386         this.picker().on('mousedown', this.onMousedown, this);
17387         this.picker().on('click', this.onClick, this);
17388         
17389         this.picker().addClass('datepicker-dropdown');
17390         
17391         this.startViewMode = this.viewMode;
17392         
17393         if(this.singleMode){
17394             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17395                 v.setVisibilityMode(Roo.Element.DISPLAY);
17396                 v.hide();
17397             });
17398             
17399             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17400                 v.setStyle('width', '189px');
17401             });
17402         }
17403         
17404         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17405             if(!this.calendarWeeks){
17406                 v.remove();
17407                 return;
17408             }
17409             
17410             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17411             v.attr('colspan', function(i, val){
17412                 return parseInt(val) + 1;
17413             });
17414         });
17415                         
17416         
17417         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17418         
17419         this.setStartDate(this.startDate);
17420         this.setEndDate(this.endDate);
17421         
17422         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17423         
17424         this.fillDow();
17425         this.fillMonths();
17426         this.update();
17427         this.showMode();
17428         
17429         if(this.isInline) {
17430             this.show();
17431         }
17432     },
17433     
17434     picker : function()
17435     {
17436         return this.pickerEl;
17437 //        return this.el.select('.datepicker', true).first();
17438     },
17439     
17440     fillDow: function()
17441     {
17442         var dowCnt = this.weekStart;
17443         
17444         var dow = {
17445             tag: 'tr',
17446             cn: [
17447                 
17448             ]
17449         };
17450         
17451         if(this.calendarWeeks){
17452             dow.cn.push({
17453                 tag: 'th',
17454                 cls: 'cw',
17455                 html: '&nbsp;'
17456             })
17457         }
17458         
17459         while (dowCnt < this.weekStart + 7) {
17460             dow.cn.push({
17461                 tag: 'th',
17462                 cls: 'dow',
17463                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17464             });
17465         }
17466         
17467         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17468     },
17469     
17470     fillMonths: function()
17471     {    
17472         var i = 0;
17473         var months = this.picker().select('>.datepicker-months td', true).first();
17474         
17475         months.dom.innerHTML = '';
17476         
17477         while (i < 12) {
17478             var month = {
17479                 tag: 'span',
17480                 cls: 'month',
17481                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17482             };
17483             
17484             months.createChild(month);
17485         }
17486         
17487     },
17488     
17489     update: function()
17490     {
17491         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;
17492         
17493         if (this.date < this.startDate) {
17494             this.viewDate = new Date(this.startDate);
17495         } else if (this.date > this.endDate) {
17496             this.viewDate = new Date(this.endDate);
17497         } else {
17498             this.viewDate = new Date(this.date);
17499         }
17500         
17501         this.fill();
17502     },
17503     
17504     fill: function() 
17505     {
17506         var d = new Date(this.viewDate),
17507                 year = d.getUTCFullYear(),
17508                 month = d.getUTCMonth(),
17509                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17510                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17511                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17512                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17513                 currentDate = this.date && this.date.valueOf(),
17514                 today = this.UTCToday();
17515         
17516         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17517         
17518 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17519         
17520 //        this.picker.select('>tfoot th.today').
17521 //                                              .text(dates[this.language].today)
17522 //                                              .toggle(this.todayBtn !== false);
17523     
17524         this.updateNavArrows();
17525         this.fillMonths();
17526                                                 
17527         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17528         
17529         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17530          
17531         prevMonth.setUTCDate(day);
17532         
17533         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17534         
17535         var nextMonth = new Date(prevMonth);
17536         
17537         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17538         
17539         nextMonth = nextMonth.valueOf();
17540         
17541         var fillMonths = false;
17542         
17543         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17544         
17545         while(prevMonth.valueOf() < nextMonth) {
17546             var clsName = '';
17547             
17548             if (prevMonth.getUTCDay() === this.weekStart) {
17549                 if(fillMonths){
17550                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17551                 }
17552                     
17553                 fillMonths = {
17554                     tag: 'tr',
17555                     cn: []
17556                 };
17557                 
17558                 if(this.calendarWeeks){
17559                     // ISO 8601: First week contains first thursday.
17560                     // ISO also states week starts on Monday, but we can be more abstract here.
17561                     var
17562                     // Start of current week: based on weekstart/current date
17563                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17564                     // Thursday of this week
17565                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17566                     // First Thursday of year, year from thursday
17567                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17568                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17569                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17570                     
17571                     fillMonths.cn.push({
17572                         tag: 'td',
17573                         cls: 'cw',
17574                         html: calWeek
17575                     });
17576                 }
17577             }
17578             
17579             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17580                 clsName += ' old';
17581             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17582                 clsName += ' new';
17583             }
17584             if (this.todayHighlight &&
17585                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17586                 prevMonth.getUTCMonth() == today.getMonth() &&
17587                 prevMonth.getUTCDate() == today.getDate()) {
17588                 clsName += ' today';
17589             }
17590             
17591             if (currentDate && prevMonth.valueOf() === currentDate) {
17592                 clsName += ' active';
17593             }
17594             
17595             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17596                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17597                     clsName += ' disabled';
17598             }
17599             
17600             fillMonths.cn.push({
17601                 tag: 'td',
17602                 cls: 'day ' + clsName,
17603                 html: prevMonth.getDate()
17604             });
17605             
17606             prevMonth.setDate(prevMonth.getDate()+1);
17607         }
17608           
17609         var currentYear = this.date && this.date.getUTCFullYear();
17610         var currentMonth = this.date && this.date.getUTCMonth();
17611         
17612         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17613         
17614         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17615             v.removeClass('active');
17616             
17617             if(currentYear === year && k === currentMonth){
17618                 v.addClass('active');
17619             }
17620             
17621             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17622                 v.addClass('disabled');
17623             }
17624             
17625         });
17626         
17627         
17628         year = parseInt(year/10, 10) * 10;
17629         
17630         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17631         
17632         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17633         
17634         year -= 1;
17635         for (var i = -1; i < 11; i++) {
17636             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17637                 tag: 'span',
17638                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17639                 html: year
17640             });
17641             
17642             year += 1;
17643         }
17644     },
17645     
17646     showMode: function(dir) 
17647     {
17648         if (dir) {
17649             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17650         }
17651         
17652         Roo.each(this.picker().select('>div',true).elements, function(v){
17653             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17654             v.hide();
17655         });
17656         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17657     },
17658     
17659     place: function()
17660     {
17661         if(this.isInline) {
17662             return;
17663         }
17664         
17665         this.picker().removeClass(['bottom', 'top']);
17666         
17667         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17668             /*
17669              * place to the top of element!
17670              *
17671              */
17672             
17673             this.picker().addClass('top');
17674             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17675             
17676             return;
17677         }
17678         
17679         this.picker().addClass('bottom');
17680         
17681         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17682     },
17683     
17684     parseDate : function(value)
17685     {
17686         if(!value || value instanceof Date){
17687             return value;
17688         }
17689         var v = Date.parseDate(value, this.format);
17690         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17691             v = Date.parseDate(value, 'Y-m-d');
17692         }
17693         if(!v && this.altFormats){
17694             if(!this.altFormatsArray){
17695                 this.altFormatsArray = this.altFormats.split("|");
17696             }
17697             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17698                 v = Date.parseDate(value, this.altFormatsArray[i]);
17699             }
17700         }
17701         return v;
17702     },
17703     
17704     formatDate : function(date, fmt)
17705     {   
17706         return (!date || !(date instanceof Date)) ?
17707         date : date.dateFormat(fmt || this.format);
17708     },
17709     
17710     onFocus : function()
17711     {
17712         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17713         this.show();
17714     },
17715     
17716     onBlur : function()
17717     {
17718         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17719         
17720         var d = this.inputEl().getValue();
17721         
17722         this.setValue(d);
17723                 
17724         this.hide();
17725     },
17726     
17727     show : function()
17728     {
17729         this.picker().show();
17730         this.update();
17731         this.place();
17732         
17733         this.fireEvent('show', this, this.date);
17734     },
17735     
17736     hide : function()
17737     {
17738         if(this.isInline) {
17739             return;
17740         }
17741         this.picker().hide();
17742         this.viewMode = this.startViewMode;
17743         this.showMode();
17744         
17745         this.fireEvent('hide', this, this.date);
17746         
17747     },
17748     
17749     onMousedown: function(e)
17750     {
17751         e.stopPropagation();
17752         e.preventDefault();
17753     },
17754     
17755     keyup: function(e)
17756     {
17757         Roo.bootstrap.DateField.superclass.keyup.call(this);
17758         this.update();
17759     },
17760
17761     setValue: function(v)
17762     {
17763         if(this.fireEvent('beforeselect', this, v) !== false){
17764             var d = new Date(this.parseDate(v) ).clearTime();
17765         
17766             if(isNaN(d.getTime())){
17767                 this.date = this.viewDate = '';
17768                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17769                 return;
17770             }
17771
17772             v = this.formatDate(d);
17773
17774             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17775
17776             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17777
17778             this.update();
17779
17780             this.fireEvent('select', this, this.date);
17781         }
17782     },
17783     
17784     getValue: function()
17785     {
17786         return this.formatDate(this.date);
17787     },
17788     
17789     fireKey: function(e)
17790     {
17791         if (!this.picker().isVisible()){
17792             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17793                 this.show();
17794             }
17795             return;
17796         }
17797         
17798         var dateChanged = false,
17799         dir, day, month,
17800         newDate, newViewDate;
17801         
17802         switch(e.keyCode){
17803             case 27: // escape
17804                 this.hide();
17805                 e.preventDefault();
17806                 break;
17807             case 37: // left
17808             case 39: // right
17809                 if (!this.keyboardNavigation) {
17810                     break;
17811                 }
17812                 dir = e.keyCode == 37 ? -1 : 1;
17813                 
17814                 if (e.ctrlKey){
17815                     newDate = this.moveYear(this.date, dir);
17816                     newViewDate = this.moveYear(this.viewDate, dir);
17817                 } else if (e.shiftKey){
17818                     newDate = this.moveMonth(this.date, dir);
17819                     newViewDate = this.moveMonth(this.viewDate, dir);
17820                 } else {
17821                     newDate = new Date(this.date);
17822                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17823                     newViewDate = new Date(this.viewDate);
17824                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17825                 }
17826                 if (this.dateWithinRange(newDate)){
17827                     this.date = newDate;
17828                     this.viewDate = newViewDate;
17829                     this.setValue(this.formatDate(this.date));
17830 //                    this.update();
17831                     e.preventDefault();
17832                     dateChanged = true;
17833                 }
17834                 break;
17835             case 38: // up
17836             case 40: // down
17837                 if (!this.keyboardNavigation) {
17838                     break;
17839                 }
17840                 dir = e.keyCode == 38 ? -1 : 1;
17841                 if (e.ctrlKey){
17842                     newDate = this.moveYear(this.date, dir);
17843                     newViewDate = this.moveYear(this.viewDate, dir);
17844                 } else if (e.shiftKey){
17845                     newDate = this.moveMonth(this.date, dir);
17846                     newViewDate = this.moveMonth(this.viewDate, dir);
17847                 } else {
17848                     newDate = new Date(this.date);
17849                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17850                     newViewDate = new Date(this.viewDate);
17851                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17852                 }
17853                 if (this.dateWithinRange(newDate)){
17854                     this.date = newDate;
17855                     this.viewDate = newViewDate;
17856                     this.setValue(this.formatDate(this.date));
17857 //                    this.update();
17858                     e.preventDefault();
17859                     dateChanged = true;
17860                 }
17861                 break;
17862             case 13: // enter
17863                 this.setValue(this.formatDate(this.date));
17864                 this.hide();
17865                 e.preventDefault();
17866                 break;
17867             case 9: // tab
17868                 this.setValue(this.formatDate(this.date));
17869                 this.hide();
17870                 break;
17871             case 16: // shift
17872             case 17: // ctrl
17873             case 18: // alt
17874                 break;
17875             default :
17876                 this.hide();
17877                 
17878         }
17879     },
17880     
17881     
17882     onClick: function(e) 
17883     {
17884         e.stopPropagation();
17885         e.preventDefault();
17886         
17887         var target = e.getTarget();
17888         
17889         if(target.nodeName.toLowerCase() === 'i'){
17890             target = Roo.get(target).dom.parentNode;
17891         }
17892         
17893         var nodeName = target.nodeName;
17894         var className = target.className;
17895         var html = target.innerHTML;
17896         //Roo.log(nodeName);
17897         
17898         switch(nodeName.toLowerCase()) {
17899             case 'th':
17900                 switch(className) {
17901                     case 'switch':
17902                         this.showMode(1);
17903                         break;
17904                     case 'prev':
17905                     case 'next':
17906                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17907                         switch(this.viewMode){
17908                                 case 0:
17909                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17910                                         break;
17911                                 case 1:
17912                                 case 2:
17913                                         this.viewDate = this.moveYear(this.viewDate, dir);
17914                                         break;
17915                         }
17916                         this.fill();
17917                         break;
17918                     case 'today':
17919                         var date = new Date();
17920                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17921 //                        this.fill()
17922                         this.setValue(this.formatDate(this.date));
17923                         
17924                         this.hide();
17925                         break;
17926                 }
17927                 break;
17928             case 'span':
17929                 if (className.indexOf('disabled') < 0) {
17930                     this.viewDate.setUTCDate(1);
17931                     if (className.indexOf('month') > -1) {
17932                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17933                     } else {
17934                         var year = parseInt(html, 10) || 0;
17935                         this.viewDate.setUTCFullYear(year);
17936                         
17937                     }
17938                     
17939                     if(this.singleMode){
17940                         this.setValue(this.formatDate(this.viewDate));
17941                         this.hide();
17942                         return;
17943                     }
17944                     
17945                     this.showMode(-1);
17946                     this.fill();
17947                 }
17948                 break;
17949                 
17950             case 'td':
17951                 //Roo.log(className);
17952                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17953                     var day = parseInt(html, 10) || 1;
17954                     var year = this.viewDate.getUTCFullYear(),
17955                         month = this.viewDate.getUTCMonth();
17956
17957                     if (className.indexOf('old') > -1) {
17958                         if(month === 0 ){
17959                             month = 11;
17960                             year -= 1;
17961                         }else{
17962                             month -= 1;
17963                         }
17964                     } else if (className.indexOf('new') > -1) {
17965                         if (month == 11) {
17966                             month = 0;
17967                             year += 1;
17968                         } else {
17969                             month += 1;
17970                         }
17971                     }
17972                     //Roo.log([year,month,day]);
17973                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17974                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17975 //                    this.fill();
17976                     //Roo.log(this.formatDate(this.date));
17977                     this.setValue(this.formatDate(this.date));
17978                     this.hide();
17979                 }
17980                 break;
17981         }
17982     },
17983     
17984     setStartDate: function(startDate)
17985     {
17986         this.startDate = startDate || -Infinity;
17987         if (this.startDate !== -Infinity) {
17988             this.startDate = this.parseDate(this.startDate);
17989         }
17990         this.update();
17991         this.updateNavArrows();
17992     },
17993
17994     setEndDate: function(endDate)
17995     {
17996         this.endDate = endDate || Infinity;
17997         if (this.endDate !== Infinity) {
17998             this.endDate = this.parseDate(this.endDate);
17999         }
18000         this.update();
18001         this.updateNavArrows();
18002     },
18003     
18004     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18005     {
18006         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18007         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18008             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18009         }
18010         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18011             return parseInt(d, 10);
18012         });
18013         this.update();
18014         this.updateNavArrows();
18015     },
18016     
18017     updateNavArrows: function() 
18018     {
18019         if(this.singleMode){
18020             return;
18021         }
18022         
18023         var d = new Date(this.viewDate),
18024         year = d.getUTCFullYear(),
18025         month = d.getUTCMonth();
18026         
18027         Roo.each(this.picker().select('.prev', true).elements, function(v){
18028             v.show();
18029             switch (this.viewMode) {
18030                 case 0:
18031
18032                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18033                         v.hide();
18034                     }
18035                     break;
18036                 case 1:
18037                 case 2:
18038                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18039                         v.hide();
18040                     }
18041                     break;
18042             }
18043         });
18044         
18045         Roo.each(this.picker().select('.next', true).elements, function(v){
18046             v.show();
18047             switch (this.viewMode) {
18048                 case 0:
18049
18050                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18051                         v.hide();
18052                     }
18053                     break;
18054                 case 1:
18055                 case 2:
18056                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18057                         v.hide();
18058                     }
18059                     break;
18060             }
18061         })
18062     },
18063     
18064     moveMonth: function(date, dir)
18065     {
18066         if (!dir) {
18067             return date;
18068         }
18069         var new_date = new Date(date.valueOf()),
18070         day = new_date.getUTCDate(),
18071         month = new_date.getUTCMonth(),
18072         mag = Math.abs(dir),
18073         new_month, test;
18074         dir = dir > 0 ? 1 : -1;
18075         if (mag == 1){
18076             test = dir == -1
18077             // If going back one month, make sure month is not current month
18078             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18079             ? function(){
18080                 return new_date.getUTCMonth() == month;
18081             }
18082             // If going forward one month, make sure month is as expected
18083             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18084             : function(){
18085                 return new_date.getUTCMonth() != new_month;
18086             };
18087             new_month = month + dir;
18088             new_date.setUTCMonth(new_month);
18089             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18090             if (new_month < 0 || new_month > 11) {
18091                 new_month = (new_month + 12) % 12;
18092             }
18093         } else {
18094             // For magnitudes >1, move one month at a time...
18095             for (var i=0; i<mag; i++) {
18096                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18097                 new_date = this.moveMonth(new_date, dir);
18098             }
18099             // ...then reset the day, keeping it in the new month
18100             new_month = new_date.getUTCMonth();
18101             new_date.setUTCDate(day);
18102             test = function(){
18103                 return new_month != new_date.getUTCMonth();
18104             };
18105         }
18106         // Common date-resetting loop -- if date is beyond end of month, make it
18107         // end of month
18108         while (test()){
18109             new_date.setUTCDate(--day);
18110             new_date.setUTCMonth(new_month);
18111         }
18112         return new_date;
18113     },
18114
18115     moveYear: function(date, dir)
18116     {
18117         return this.moveMonth(date, dir*12);
18118     },
18119
18120     dateWithinRange: function(date)
18121     {
18122         return date >= this.startDate && date <= this.endDate;
18123     },
18124
18125     
18126     remove: function() 
18127     {
18128         this.picker().remove();
18129     },
18130     
18131     validateValue : function(value)
18132     {
18133         if(value.length < 1)  {
18134             if(this.allowBlank){
18135                 return true;
18136             }
18137             return false;
18138         }
18139         
18140         if(value.length < this.minLength){
18141             return false;
18142         }
18143         if(value.length > this.maxLength){
18144             return false;
18145         }
18146         if(this.vtype){
18147             var vt = Roo.form.VTypes;
18148             if(!vt[this.vtype](value, this)){
18149                 return false;
18150             }
18151         }
18152         if(typeof this.validator == "function"){
18153             var msg = this.validator(value);
18154             if(msg !== true){
18155                 return false;
18156             }
18157         }
18158         
18159         if(this.regex && !this.regex.test(value)){
18160             return false;
18161         }
18162         
18163         if(typeof(this.parseDate(value)) == 'undefined'){
18164             return false;
18165         }
18166         
18167         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18168             return false;
18169         }      
18170         
18171         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18172             return false;
18173         } 
18174         
18175         
18176         return true;
18177     }
18178    
18179 });
18180
18181 Roo.apply(Roo.bootstrap.DateField,  {
18182     
18183     head : {
18184         tag: 'thead',
18185         cn: [
18186         {
18187             tag: 'tr',
18188             cn: [
18189             {
18190                 tag: 'th',
18191                 cls: 'prev',
18192                 html: '<i class="fa fa-arrow-left"/>'
18193             },
18194             {
18195                 tag: 'th',
18196                 cls: 'switch',
18197                 colspan: '5'
18198             },
18199             {
18200                 tag: 'th',
18201                 cls: 'next',
18202                 html: '<i class="fa fa-arrow-right"/>'
18203             }
18204
18205             ]
18206         }
18207         ]
18208     },
18209     
18210     content : {
18211         tag: 'tbody',
18212         cn: [
18213         {
18214             tag: 'tr',
18215             cn: [
18216             {
18217                 tag: 'td',
18218                 colspan: '7'
18219             }
18220             ]
18221         }
18222         ]
18223     },
18224     
18225     footer : {
18226         tag: 'tfoot',
18227         cn: [
18228         {
18229             tag: 'tr',
18230             cn: [
18231             {
18232                 tag: 'th',
18233                 colspan: '7',
18234                 cls: 'today'
18235             }
18236                     
18237             ]
18238         }
18239         ]
18240     },
18241     
18242     dates:{
18243         en: {
18244             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18245             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18246             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18247             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18248             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18249             today: "Today"
18250         }
18251     },
18252     
18253     modes: [
18254     {
18255         clsName: 'days',
18256         navFnc: 'Month',
18257         navStep: 1
18258     },
18259     {
18260         clsName: 'months',
18261         navFnc: 'FullYear',
18262         navStep: 1
18263     },
18264     {
18265         clsName: 'years',
18266         navFnc: 'FullYear',
18267         navStep: 10
18268     }]
18269 });
18270
18271 Roo.apply(Roo.bootstrap.DateField,  {
18272   
18273     template : {
18274         tag: 'div',
18275         cls: 'datepicker dropdown-menu roo-dynamic',
18276         cn: [
18277         {
18278             tag: 'div',
18279             cls: 'datepicker-days',
18280             cn: [
18281             {
18282                 tag: 'table',
18283                 cls: 'table-condensed',
18284                 cn:[
18285                 Roo.bootstrap.DateField.head,
18286                 {
18287                     tag: 'tbody'
18288                 },
18289                 Roo.bootstrap.DateField.footer
18290                 ]
18291             }
18292             ]
18293         },
18294         {
18295             tag: 'div',
18296             cls: 'datepicker-months',
18297             cn: [
18298             {
18299                 tag: 'table',
18300                 cls: 'table-condensed',
18301                 cn:[
18302                 Roo.bootstrap.DateField.head,
18303                 Roo.bootstrap.DateField.content,
18304                 Roo.bootstrap.DateField.footer
18305                 ]
18306             }
18307             ]
18308         },
18309         {
18310             tag: 'div',
18311             cls: 'datepicker-years',
18312             cn: [
18313             {
18314                 tag: 'table',
18315                 cls: 'table-condensed',
18316                 cn:[
18317                 Roo.bootstrap.DateField.head,
18318                 Roo.bootstrap.DateField.content,
18319                 Roo.bootstrap.DateField.footer
18320                 ]
18321             }
18322             ]
18323         }
18324         ]
18325     }
18326 });
18327
18328  
18329
18330  /*
18331  * - LGPL
18332  *
18333  * TimeField
18334  * 
18335  */
18336
18337 /**
18338  * @class Roo.bootstrap.TimeField
18339  * @extends Roo.bootstrap.Input
18340  * Bootstrap DateField class
18341  * 
18342  * 
18343  * @constructor
18344  * Create a new TimeField
18345  * @param {Object} config The config object
18346  */
18347
18348 Roo.bootstrap.TimeField = function(config){
18349     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18350     this.addEvents({
18351             /**
18352              * @event show
18353              * Fires when this field show.
18354              * @param {Roo.bootstrap.DateField} thisthis
18355              * @param {Mixed} date The date value
18356              */
18357             show : true,
18358             /**
18359              * @event show
18360              * Fires when this field hide.
18361              * @param {Roo.bootstrap.DateField} this
18362              * @param {Mixed} date The date value
18363              */
18364             hide : true,
18365             /**
18366              * @event select
18367              * Fires when select a date.
18368              * @param {Roo.bootstrap.DateField} this
18369              * @param {Mixed} date The date value
18370              */
18371             select : true
18372         });
18373 };
18374
18375 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18376     
18377     /**
18378      * @cfg {String} format
18379      * The default time format string which can be overriden for localization support.  The format must be
18380      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18381      */
18382     format : "H:i",
18383        
18384     onRender: function(ct, position)
18385     {
18386         
18387         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18388                 
18389         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18390         
18391         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18392         
18393         this.pop = this.picker().select('>.datepicker-time',true).first();
18394         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18395         
18396         this.picker().on('mousedown', this.onMousedown, this);
18397         this.picker().on('click', this.onClick, this);
18398         
18399         this.picker().addClass('datepicker-dropdown');
18400     
18401         this.fillTime();
18402         this.update();
18403             
18404         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18405         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18406         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18407         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18408         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18409         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18410
18411     },
18412     
18413     fireKey: function(e){
18414         if (!this.picker().isVisible()){
18415             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18416                 this.show();
18417             }
18418             return;
18419         }
18420
18421         e.preventDefault();
18422         
18423         switch(e.keyCode){
18424             case 27: // escape
18425                 this.hide();
18426                 break;
18427             case 37: // left
18428             case 39: // right
18429                 this.onTogglePeriod();
18430                 break;
18431             case 38: // up
18432                 this.onIncrementMinutes();
18433                 break;
18434             case 40: // down
18435                 this.onDecrementMinutes();
18436                 break;
18437             case 13: // enter
18438             case 9: // tab
18439                 this.setTime();
18440                 break;
18441         }
18442     },
18443     
18444     onClick: function(e) {
18445         e.stopPropagation();
18446         e.preventDefault();
18447     },
18448     
18449     picker : function()
18450     {
18451         return this.el.select('.datepicker', true).first();
18452     },
18453     
18454     fillTime: function()
18455     {    
18456         var time = this.pop.select('tbody', true).first();
18457         
18458         time.dom.innerHTML = '';
18459         
18460         time.createChild({
18461             tag: 'tr',
18462             cn: [
18463                 {
18464                     tag: 'td',
18465                     cn: [
18466                         {
18467                             tag: 'a',
18468                             href: '#',
18469                             cls: 'btn',
18470                             cn: [
18471                                 {
18472                                     tag: 'span',
18473                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18474                                 }
18475                             ]
18476                         } 
18477                     ]
18478                 },
18479                 {
18480                     tag: 'td',
18481                     cls: 'separator'
18482                 },
18483                 {
18484                     tag: 'td',
18485                     cn: [
18486                         {
18487                             tag: 'a',
18488                             href: '#',
18489                             cls: 'btn',
18490                             cn: [
18491                                 {
18492                                     tag: 'span',
18493                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18494                                 }
18495                             ]
18496                         }
18497                     ]
18498                 },
18499                 {
18500                     tag: 'td',
18501                     cls: 'separator'
18502                 }
18503             ]
18504         });
18505         
18506         time.createChild({
18507             tag: 'tr',
18508             cn: [
18509                 {
18510                     tag: 'td',
18511                     cn: [
18512                         {
18513                             tag: 'span',
18514                             cls: 'timepicker-hour',
18515                             html: '00'
18516                         }  
18517                     ]
18518                 },
18519                 {
18520                     tag: 'td',
18521                     cls: 'separator',
18522                     html: ':'
18523                 },
18524                 {
18525                     tag: 'td',
18526                     cn: [
18527                         {
18528                             tag: 'span',
18529                             cls: 'timepicker-minute',
18530                             html: '00'
18531                         }  
18532                     ]
18533                 },
18534                 {
18535                     tag: 'td',
18536                     cls: 'separator'
18537                 },
18538                 {
18539                     tag: 'td',
18540                     cn: [
18541                         {
18542                             tag: 'button',
18543                             type: 'button',
18544                             cls: 'btn btn-primary period',
18545                             html: 'AM'
18546                             
18547                         }
18548                     ]
18549                 }
18550             ]
18551         });
18552         
18553         time.createChild({
18554             tag: 'tr',
18555             cn: [
18556                 {
18557                     tag: 'td',
18558                     cn: [
18559                         {
18560                             tag: 'a',
18561                             href: '#',
18562                             cls: 'btn',
18563                             cn: [
18564                                 {
18565                                     tag: 'span',
18566                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18567                                 }
18568                             ]
18569                         }
18570                     ]
18571                 },
18572                 {
18573                     tag: 'td',
18574                     cls: 'separator'
18575                 },
18576                 {
18577                     tag: 'td',
18578                     cn: [
18579                         {
18580                             tag: 'a',
18581                             href: '#',
18582                             cls: 'btn',
18583                             cn: [
18584                                 {
18585                                     tag: 'span',
18586                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18587                                 }
18588                             ]
18589                         }
18590                     ]
18591                 },
18592                 {
18593                     tag: 'td',
18594                     cls: 'separator'
18595                 }
18596             ]
18597         });
18598         
18599     },
18600     
18601     update: function()
18602     {
18603         
18604         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18605         
18606         this.fill();
18607     },
18608     
18609     fill: function() 
18610     {
18611         var hours = this.time.getHours();
18612         var minutes = this.time.getMinutes();
18613         var period = 'AM';
18614         
18615         if(hours > 11){
18616             period = 'PM';
18617         }
18618         
18619         if(hours == 0){
18620             hours = 12;
18621         }
18622         
18623         
18624         if(hours > 12){
18625             hours = hours - 12;
18626         }
18627         
18628         if(hours < 10){
18629             hours = '0' + hours;
18630         }
18631         
18632         if(minutes < 10){
18633             minutes = '0' + minutes;
18634         }
18635         
18636         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18637         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18638         this.pop.select('button', true).first().dom.innerHTML = period;
18639         
18640     },
18641     
18642     place: function()
18643     {   
18644         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18645         
18646         var cls = ['bottom'];
18647         
18648         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18649             cls.pop();
18650             cls.push('top');
18651         }
18652         
18653         cls.push('right');
18654         
18655         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18656             cls.pop();
18657             cls.push('left');
18658         }
18659         
18660         this.picker().addClass(cls.join('-'));
18661         
18662         var _this = this;
18663         
18664         Roo.each(cls, function(c){
18665             if(c == 'bottom'){
18666                 _this.picker().setTop(_this.inputEl().getHeight());
18667                 return;
18668             }
18669             if(c == 'top'){
18670                 _this.picker().setTop(0 - _this.picker().getHeight());
18671                 return;
18672             }
18673             
18674             if(c == 'left'){
18675                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18676                 return;
18677             }
18678             if(c == 'right'){
18679                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18680                 return;
18681             }
18682         });
18683         
18684     },
18685   
18686     onFocus : function()
18687     {
18688         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18689         this.show();
18690     },
18691     
18692     onBlur : function()
18693     {
18694         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18695         this.hide();
18696     },
18697     
18698     show : function()
18699     {
18700         this.picker().show();
18701         this.pop.show();
18702         this.update();
18703         this.place();
18704         
18705         this.fireEvent('show', this, this.date);
18706     },
18707     
18708     hide : function()
18709     {
18710         this.picker().hide();
18711         this.pop.hide();
18712         
18713         this.fireEvent('hide', this, this.date);
18714     },
18715     
18716     setTime : function()
18717     {
18718         this.hide();
18719         this.setValue(this.time.format(this.format));
18720         
18721         this.fireEvent('select', this, this.date);
18722         
18723         
18724     },
18725     
18726     onMousedown: function(e){
18727         e.stopPropagation();
18728         e.preventDefault();
18729     },
18730     
18731     onIncrementHours: function()
18732     {
18733         Roo.log('onIncrementHours');
18734         this.time = this.time.add(Date.HOUR, 1);
18735         this.update();
18736         
18737     },
18738     
18739     onDecrementHours: function()
18740     {
18741         Roo.log('onDecrementHours');
18742         this.time = this.time.add(Date.HOUR, -1);
18743         this.update();
18744     },
18745     
18746     onIncrementMinutes: function()
18747     {
18748         Roo.log('onIncrementMinutes');
18749         this.time = this.time.add(Date.MINUTE, 1);
18750         this.update();
18751     },
18752     
18753     onDecrementMinutes: function()
18754     {
18755         Roo.log('onDecrementMinutes');
18756         this.time = this.time.add(Date.MINUTE, -1);
18757         this.update();
18758     },
18759     
18760     onTogglePeriod: function()
18761     {
18762         Roo.log('onTogglePeriod');
18763         this.time = this.time.add(Date.HOUR, 12);
18764         this.update();
18765     }
18766     
18767    
18768 });
18769
18770 Roo.apply(Roo.bootstrap.TimeField,  {
18771     
18772     content : {
18773         tag: 'tbody',
18774         cn: [
18775             {
18776                 tag: 'tr',
18777                 cn: [
18778                 {
18779                     tag: 'td',
18780                     colspan: '7'
18781                 }
18782                 ]
18783             }
18784         ]
18785     },
18786     
18787     footer : {
18788         tag: 'tfoot',
18789         cn: [
18790             {
18791                 tag: 'tr',
18792                 cn: [
18793                 {
18794                     tag: 'th',
18795                     colspan: '7',
18796                     cls: '',
18797                     cn: [
18798                         {
18799                             tag: 'button',
18800                             cls: 'btn btn-info ok',
18801                             html: 'OK'
18802                         }
18803                     ]
18804                 }
18805
18806                 ]
18807             }
18808         ]
18809     }
18810 });
18811
18812 Roo.apply(Roo.bootstrap.TimeField,  {
18813   
18814     template : {
18815         tag: 'div',
18816         cls: 'datepicker dropdown-menu',
18817         cn: [
18818             {
18819                 tag: 'div',
18820                 cls: 'datepicker-time',
18821                 cn: [
18822                 {
18823                     tag: 'table',
18824                     cls: 'table-condensed',
18825                     cn:[
18826                     Roo.bootstrap.TimeField.content,
18827                     Roo.bootstrap.TimeField.footer
18828                     ]
18829                 }
18830                 ]
18831             }
18832         ]
18833     }
18834 });
18835
18836  
18837
18838  /*
18839  * - LGPL
18840  *
18841  * MonthField
18842  * 
18843  */
18844
18845 /**
18846  * @class Roo.bootstrap.MonthField
18847  * @extends Roo.bootstrap.Input
18848  * Bootstrap MonthField class
18849  * 
18850  * @cfg {String} language default en
18851  * 
18852  * @constructor
18853  * Create a new MonthField
18854  * @param {Object} config The config object
18855  */
18856
18857 Roo.bootstrap.MonthField = function(config){
18858     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18859     
18860     this.addEvents({
18861         /**
18862          * @event show
18863          * Fires when this field show.
18864          * @param {Roo.bootstrap.MonthField} this
18865          * @param {Mixed} date The date value
18866          */
18867         show : true,
18868         /**
18869          * @event show
18870          * Fires when this field hide.
18871          * @param {Roo.bootstrap.MonthField} this
18872          * @param {Mixed} date The date value
18873          */
18874         hide : true,
18875         /**
18876          * @event select
18877          * Fires when select a date.
18878          * @param {Roo.bootstrap.MonthField} this
18879          * @param {String} oldvalue The old value
18880          * @param {String} newvalue The new value
18881          */
18882         select : true
18883     });
18884 };
18885
18886 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18887     
18888     onRender: function(ct, position)
18889     {
18890         
18891         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18892         
18893         this.language = this.language || 'en';
18894         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18895         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18896         
18897         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18898         this.isInline = false;
18899         this.isInput = true;
18900         this.component = this.el.select('.add-on', true).first() || false;
18901         this.component = (this.component && this.component.length === 0) ? false : this.component;
18902         this.hasInput = this.component && this.inputEL().length;
18903         
18904         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18905         
18906         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18907         
18908         this.picker().on('mousedown', this.onMousedown, this);
18909         this.picker().on('click', this.onClick, this);
18910         
18911         this.picker().addClass('datepicker-dropdown');
18912         
18913         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18914             v.setStyle('width', '189px');
18915         });
18916         
18917         this.fillMonths();
18918         
18919         this.update();
18920         
18921         if(this.isInline) {
18922             this.show();
18923         }
18924         
18925     },
18926     
18927     setValue: function(v, suppressEvent)
18928     {   
18929         var o = this.getValue();
18930         
18931         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18932         
18933         this.update();
18934
18935         if(suppressEvent !== true){
18936             this.fireEvent('select', this, o, v);
18937         }
18938         
18939     },
18940     
18941     getValue: function()
18942     {
18943         return this.value;
18944     },
18945     
18946     onClick: function(e) 
18947     {
18948         e.stopPropagation();
18949         e.preventDefault();
18950         
18951         var target = e.getTarget();
18952         
18953         if(target.nodeName.toLowerCase() === 'i'){
18954             target = Roo.get(target).dom.parentNode;
18955         }
18956         
18957         var nodeName = target.nodeName;
18958         var className = target.className;
18959         var html = target.innerHTML;
18960         
18961         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18962             return;
18963         }
18964         
18965         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18966         
18967         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18968         
18969         this.hide();
18970                         
18971     },
18972     
18973     picker : function()
18974     {
18975         return this.pickerEl;
18976     },
18977     
18978     fillMonths: function()
18979     {    
18980         var i = 0;
18981         var months = this.picker().select('>.datepicker-months td', true).first();
18982         
18983         months.dom.innerHTML = '';
18984         
18985         while (i < 12) {
18986             var month = {
18987                 tag: 'span',
18988                 cls: 'month',
18989                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18990             };
18991             
18992             months.createChild(month);
18993         }
18994         
18995     },
18996     
18997     update: function()
18998     {
18999         var _this = this;
19000         
19001         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19002             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19003         }
19004         
19005         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19006             e.removeClass('active');
19007             
19008             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19009                 e.addClass('active');
19010             }
19011         })
19012     },
19013     
19014     place: function()
19015     {
19016         if(this.isInline) {
19017             return;
19018         }
19019         
19020         this.picker().removeClass(['bottom', 'top']);
19021         
19022         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19023             /*
19024              * place to the top of element!
19025              *
19026              */
19027             
19028             this.picker().addClass('top');
19029             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19030             
19031             return;
19032         }
19033         
19034         this.picker().addClass('bottom');
19035         
19036         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19037     },
19038     
19039     onFocus : function()
19040     {
19041         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19042         this.show();
19043     },
19044     
19045     onBlur : function()
19046     {
19047         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19048         
19049         var d = this.inputEl().getValue();
19050         
19051         this.setValue(d);
19052                 
19053         this.hide();
19054     },
19055     
19056     show : function()
19057     {
19058         this.picker().show();
19059         this.picker().select('>.datepicker-months', true).first().show();
19060         this.update();
19061         this.place();
19062         
19063         this.fireEvent('show', this, this.date);
19064     },
19065     
19066     hide : function()
19067     {
19068         if(this.isInline) {
19069             return;
19070         }
19071         this.picker().hide();
19072         this.fireEvent('hide', this, this.date);
19073         
19074     },
19075     
19076     onMousedown: function(e)
19077     {
19078         e.stopPropagation();
19079         e.preventDefault();
19080     },
19081     
19082     keyup: function(e)
19083     {
19084         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19085         this.update();
19086     },
19087
19088     fireKey: function(e)
19089     {
19090         if (!this.picker().isVisible()){
19091             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19092                 this.show();
19093             }
19094             return;
19095         }
19096         
19097         var dir;
19098         
19099         switch(e.keyCode){
19100             case 27: // escape
19101                 this.hide();
19102                 e.preventDefault();
19103                 break;
19104             case 37: // left
19105             case 39: // right
19106                 dir = e.keyCode == 37 ? -1 : 1;
19107                 
19108                 this.vIndex = this.vIndex + dir;
19109                 
19110                 if(this.vIndex < 0){
19111                     this.vIndex = 0;
19112                 }
19113                 
19114                 if(this.vIndex > 11){
19115                     this.vIndex = 11;
19116                 }
19117                 
19118                 if(isNaN(this.vIndex)){
19119                     this.vIndex = 0;
19120                 }
19121                 
19122                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19123                 
19124                 break;
19125             case 38: // up
19126             case 40: // down
19127                 
19128                 dir = e.keyCode == 38 ? -1 : 1;
19129                 
19130                 this.vIndex = this.vIndex + dir * 4;
19131                 
19132                 if(this.vIndex < 0){
19133                     this.vIndex = 0;
19134                 }
19135                 
19136                 if(this.vIndex > 11){
19137                     this.vIndex = 11;
19138                 }
19139                 
19140                 if(isNaN(this.vIndex)){
19141                     this.vIndex = 0;
19142                 }
19143                 
19144                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19145                 break;
19146                 
19147             case 13: // enter
19148                 
19149                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19150                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19151                 }
19152                 
19153                 this.hide();
19154                 e.preventDefault();
19155                 break;
19156             case 9: // tab
19157                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19158                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19159                 }
19160                 this.hide();
19161                 break;
19162             case 16: // shift
19163             case 17: // ctrl
19164             case 18: // alt
19165                 break;
19166             default :
19167                 this.hide();
19168                 
19169         }
19170     },
19171     
19172     remove: function() 
19173     {
19174         this.picker().remove();
19175     }
19176    
19177 });
19178
19179 Roo.apply(Roo.bootstrap.MonthField,  {
19180     
19181     content : {
19182         tag: 'tbody',
19183         cn: [
19184         {
19185             tag: 'tr',
19186             cn: [
19187             {
19188                 tag: 'td',
19189                 colspan: '7'
19190             }
19191             ]
19192         }
19193         ]
19194     },
19195     
19196     dates:{
19197         en: {
19198             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19199             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19200         }
19201     }
19202 });
19203
19204 Roo.apply(Roo.bootstrap.MonthField,  {
19205   
19206     template : {
19207         tag: 'div',
19208         cls: 'datepicker dropdown-menu roo-dynamic',
19209         cn: [
19210             {
19211                 tag: 'div',
19212                 cls: 'datepicker-months',
19213                 cn: [
19214                 {
19215                     tag: 'table',
19216                     cls: 'table-condensed',
19217                     cn:[
19218                         Roo.bootstrap.DateField.content
19219                     ]
19220                 }
19221                 ]
19222             }
19223         ]
19224     }
19225 });
19226
19227  
19228
19229  
19230  /*
19231  * - LGPL
19232  *
19233  * CheckBox
19234  * 
19235  */
19236
19237 /**
19238  * @class Roo.bootstrap.CheckBox
19239  * @extends Roo.bootstrap.Input
19240  * Bootstrap CheckBox class
19241  * 
19242  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19243  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19244  * @cfg {String} boxLabel The text that appears beside the checkbox
19245  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19246  * @cfg {Boolean} checked initnal the element
19247  * @cfg {Boolean} inline inline the element (default false)
19248  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19249  * 
19250  * @constructor
19251  * Create a new CheckBox
19252  * @param {Object} config The config object
19253  */
19254
19255 Roo.bootstrap.CheckBox = function(config){
19256     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19257    
19258     this.addEvents({
19259         /**
19260         * @event check
19261         * Fires when the element is checked or unchecked.
19262         * @param {Roo.bootstrap.CheckBox} this This input
19263         * @param {Boolean} checked The new checked value
19264         */
19265        check : true
19266     });
19267     
19268 };
19269
19270 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19271   
19272     inputType: 'checkbox',
19273     inputValue: 1,
19274     valueOff: 0,
19275     boxLabel: false,
19276     checked: false,
19277     weight : false,
19278     inline: false,
19279     
19280     getAutoCreate : function()
19281     {
19282         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19283         
19284         var id = Roo.id();
19285         
19286         var cfg = {};
19287         
19288         cfg.cls = 'form-group ' + this.inputType; //input-group
19289         
19290         if(this.inline){
19291             cfg.cls += ' ' + this.inputType + '-inline';
19292         }
19293         
19294         var input =  {
19295             tag: 'input',
19296             id : id,
19297             type : this.inputType,
19298             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19299             cls : 'roo-' + this.inputType, //'form-box',
19300             placeholder : this.placeholder || ''
19301             
19302         };
19303         
19304         if (this.weight) { // Validity check?
19305             cfg.cls += " " + this.inputType + "-" + this.weight;
19306         }
19307         
19308         if (this.disabled) {
19309             input.disabled=true;
19310         }
19311         
19312         if(this.checked){
19313             input.checked = this.checked;
19314         }
19315         
19316         if (this.name) {
19317             input.name = this.name;
19318         }
19319         
19320         if (this.size) {
19321             input.cls += ' input-' + this.size;
19322         }
19323         
19324         var settings=this;
19325         
19326         ['xs','sm','md','lg'].map(function(size){
19327             if (settings[size]) {
19328                 cfg.cls += ' col-' + size + '-' + settings[size];
19329             }
19330         });
19331         
19332         var inputblock = input;
19333          
19334         if (this.before || this.after) {
19335             
19336             inputblock = {
19337                 cls : 'input-group',
19338                 cn :  [] 
19339             };
19340             
19341             if (this.before) {
19342                 inputblock.cn.push({
19343                     tag :'span',
19344                     cls : 'input-group-addon',
19345                     html : this.before
19346                 });
19347             }
19348             
19349             inputblock.cn.push(input);
19350             
19351             if (this.after) {
19352                 inputblock.cn.push({
19353                     tag :'span',
19354                     cls : 'input-group-addon',
19355                     html : this.after
19356                 });
19357             }
19358             
19359         }
19360         
19361         if (align ==='left' && this.fieldLabel.length) {
19362 //                Roo.log("left and has label");
19363                 cfg.cn = [
19364                     
19365                     {
19366                         tag: 'label',
19367                         'for' :  id,
19368                         cls : 'control-label col-md-' + this.labelWidth,
19369                         html : this.fieldLabel
19370                         
19371                     },
19372                     {
19373                         cls : "col-md-" + (12 - this.labelWidth), 
19374                         cn: [
19375                             inputblock
19376                         ]
19377                     }
19378                     
19379                 ];
19380         } else if ( this.fieldLabel.length) {
19381 //                Roo.log(" label");
19382                 cfg.cn = [
19383                    
19384                     {
19385                         tag: this.boxLabel ? 'span' : 'label',
19386                         'for': id,
19387                         cls: 'control-label box-input-label',
19388                         //cls : 'input-group-addon',
19389                         html : this.fieldLabel
19390                         
19391                     },
19392                     
19393                     inputblock
19394                     
19395                 ];
19396
19397         } else {
19398             
19399 //                Roo.log(" no label && no align");
19400                 cfg.cn = [  inputblock ] ;
19401                 
19402                 
19403         }
19404         
19405         if(this.boxLabel){
19406              var boxLabelCfg = {
19407                 tag: 'label',
19408                 //'for': id, // box label is handled by onclick - so no for...
19409                 cls: 'box-label',
19410                 html: this.boxLabel
19411             };
19412             
19413             if(this.tooltip){
19414                 boxLabelCfg.tooltip = this.tooltip;
19415             }
19416              
19417             cfg.cn.push(boxLabelCfg);
19418         }
19419         
19420         
19421        
19422         return cfg;
19423         
19424     },
19425     
19426     /**
19427      * return the real input element.
19428      */
19429     inputEl: function ()
19430     {
19431         return this.el.select('input.roo-' + this.inputType,true).first();
19432     },
19433     
19434     labelEl: function()
19435     {
19436         return this.el.select('label.control-label',true).first();
19437     },
19438     /* depricated... */
19439     
19440     label: function()
19441     {
19442         return this.labelEl();
19443     },
19444     
19445     boxLabelEl: function()
19446     {
19447         return this.el.select('label.box-label',true).first();
19448     },
19449     
19450     initEvents : function()
19451     {
19452 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19453         
19454         this.inputEl().on('click', this.onClick,  this);
19455         
19456         if (this.boxLabel) { 
19457             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19458         }
19459         
19460         this.startValue = this.getValue();
19461         
19462         if(this.groupId){
19463             Roo.bootstrap.CheckBox.register(this);
19464         }
19465     },
19466     
19467     onClick : function()
19468     {   
19469         this.setChecked(!this.checked);
19470     },
19471     
19472     setChecked : function(state,suppressEvent)
19473     {
19474         this.startValue = this.getValue();
19475         
19476         if(this.inputType == 'radio'){
19477             
19478             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19479                 e.dom.checked = false;
19480             });
19481             
19482             this.inputEl().dom.checked = true;
19483             
19484             this.inputEl().dom.value = this.inputValue;
19485             
19486             if(suppressEvent !== true){
19487                 this.fireEvent('check', this, true);
19488             }
19489             
19490             this.validate();
19491             
19492             return;
19493         }
19494         
19495         this.checked = state;
19496         
19497         this.inputEl().dom.checked = state;
19498         
19499         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19500         
19501         if(suppressEvent !== true){
19502             this.fireEvent('check', this, state);
19503         }
19504         
19505         this.validate();
19506     },
19507     
19508     getValue : function()
19509     {
19510         if(this.inputType == 'radio'){
19511             return this.getGroupValue();
19512         }
19513         
19514         return this.inputEl().getValue();
19515         
19516     },
19517     
19518     getGroupValue : function()
19519     {
19520         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19521             return '';
19522         }
19523         
19524         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19525     },
19526     
19527     setValue : function(v,suppressEvent)
19528     {
19529         if(this.inputType == 'radio'){
19530             this.setGroupValue(v, suppressEvent);
19531             return;
19532         }
19533         
19534         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19535         
19536         this.validate();
19537     },
19538     
19539     setGroupValue : function(v, suppressEvent)
19540     {
19541         this.startValue = this.getValue();
19542         
19543         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19544             e.dom.checked = false;
19545             
19546             if(e.dom.value == v){
19547                 e.dom.checked = true;
19548             }
19549         });
19550         
19551         if(suppressEvent !== true){
19552             this.fireEvent('check', this, true);
19553         }
19554
19555         this.validate();
19556         
19557         return;
19558     },
19559     
19560     validate : function()
19561     {
19562         if(
19563                 this.disabled || 
19564                 (this.inputType == 'radio' && this.validateRadio()) ||
19565                 (this.inputType == 'checkbox' && this.validateCheckbox())
19566         ){
19567             this.markValid();
19568             return true;
19569         }
19570         
19571         this.markInvalid();
19572         return false;
19573     },
19574     
19575     validateRadio : function()
19576     {
19577         if(this.allowBlank){
19578             return true;
19579         }
19580         
19581         var valid = false;
19582         
19583         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19584             if(!e.dom.checked){
19585                 return;
19586             }
19587             
19588             valid = true;
19589             
19590             return false;
19591         });
19592         
19593         return valid;
19594     },
19595     
19596     validateCheckbox : function()
19597     {
19598         if(!this.groupId){
19599             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19600         }
19601         
19602         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19603         
19604         if(!group){
19605             return false;
19606         }
19607         
19608         var r = false;
19609         
19610         for(var i in group){
19611             if(r){
19612                 break;
19613             }
19614             
19615             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19616         }
19617         
19618         return r;
19619     },
19620     
19621     /**
19622      * Mark this field as valid
19623      */
19624     markValid : function()
19625     {
19626         if(this.allowBlank){
19627             return;
19628         }
19629         
19630         var _this = this;
19631         
19632         this.fireEvent('valid', this);
19633         
19634         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19635         
19636         if(this.groupId){
19637             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19638         }
19639         
19640         if(label){
19641             label.markValid();
19642         }
19643         
19644         if(this.inputType == 'radio'){
19645             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19646                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19647                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19648             });
19649             
19650             return;
19651         }
19652         
19653         if(!this.groupId){
19654             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19655             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19656             return;
19657         }
19658         
19659         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19660             
19661         if(!group){
19662             return;
19663         }
19664         
19665         for(var i in group){
19666             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19667             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19668         }
19669     },
19670     
19671      /**
19672      * Mark this field as invalid
19673      * @param {String} msg The validation message
19674      */
19675     markInvalid : function(msg)
19676     {
19677         if(this.allowBlank){
19678             return;
19679         }
19680         
19681         var _this = this;
19682         
19683         this.fireEvent('invalid', this, msg);
19684         
19685         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19686         
19687         if(this.groupId){
19688             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19689         }
19690         
19691         if(label){
19692             label.markInvalid();
19693         }
19694             
19695         if(this.inputType == 'radio'){
19696             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19697                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19698                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19699             });
19700             
19701             return;
19702         }
19703         
19704         if(!this.groupId){
19705             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19706             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19707             return;
19708         }
19709         
19710         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19711         
19712         if(!group){
19713             return;
19714         }
19715         
19716         for(var i in group){
19717             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19718             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19719         }
19720         
19721     },
19722     
19723     disable : function()
19724     {
19725         if(this.inputType != 'radio'){
19726             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19727             return;
19728         }
19729         
19730         var _this = this;
19731         
19732         if(this.rendered){
19733             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19734                 _this.getActionEl().addClass(this.disabledClass);
19735                 e.dom.disabled = true;
19736             });
19737         }
19738         
19739         this.disabled = true;
19740         this.fireEvent("disable", this);
19741         return this;
19742     },
19743
19744     enable : function()
19745     {
19746         if(this.inputType != 'radio'){
19747             Roo.bootstrap.CheckBox.superclass.enable.call(this);
19748             return;
19749         }
19750         
19751         var _this = this;
19752         
19753         if(this.rendered){
19754             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19755                 _this.getActionEl().removeClass(this.disabledClass);
19756                 e.dom.disabled = false;
19757             });
19758         }
19759         
19760         this.disabled = false;
19761         this.fireEvent("enable", this);
19762         return this;
19763     }
19764     
19765
19766 });
19767
19768 Roo.apply(Roo.bootstrap.CheckBox, {
19769     
19770     groups: {},
19771     
19772      /**
19773     * register a CheckBox Group
19774     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19775     */
19776     register : function(checkbox)
19777     {
19778         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19779             this.groups[checkbox.groupId] = {};
19780         }
19781         
19782         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19783             return;
19784         }
19785         
19786         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19787         
19788     },
19789     /**
19790     * fetch a CheckBox Group based on the group ID
19791     * @param {string} the group ID
19792     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19793     */
19794     get: function(groupId) {
19795         if (typeof(this.groups[groupId]) == 'undefined') {
19796             return false;
19797         }
19798         
19799         return this.groups[groupId] ;
19800     }
19801     
19802     
19803 });
19804 /*
19805  * - LGPL
19806  *
19807  * Radio
19808  *
19809  *
19810  * not inline
19811  *<div class="radio">
19812   <label>
19813     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19814     Option one is this and that&mdash;be sure to include why it's great
19815   </label>
19816 </div>
19817  *
19818  *
19819  *inline
19820  *<span>
19821  *<label class="radio-inline">fieldLabel</label>
19822  *<label class="radio-inline">
19823   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19824 </label>
19825 <span>
19826  * 
19827  * 
19828  */
19829
19830 /**
19831  * @class Roo.bootstrap.Radio
19832  * @extends Roo.bootstrap.CheckBox
19833  * Bootstrap Radio class
19834
19835  * @constructor
19836  * Create a new Radio
19837  * @param {Object} config The config object
19838  */
19839
19840 Roo.bootstrap.Radio = function(config){
19841     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19842    
19843 };
19844
19845 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19846     
19847     inputType: 'radio',
19848     inputValue: '',
19849     valueOff: '',
19850     
19851     getAutoCreate : function()
19852     {
19853         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19854         align = align || 'left'; // default...
19855         
19856         
19857         
19858         var id = Roo.id();
19859         
19860         var cfg = {
19861                 tag : this.inline ? 'span' : 'div',
19862                 cls : '',
19863                 cn : []
19864         };
19865         
19866         var inline = this.inline ? ' radio-inline' : '';
19867         
19868         var lbl = {
19869                 tag: 'label' ,
19870                 // does not need for, as we wrap the input with it..
19871                 'for' : id,
19872                 cls : 'control-label box-label' + inline,
19873                 cn : []
19874         };
19875         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19876         
19877         var fieldLabel = {
19878             tag: 'label' ,
19879             //cls : 'control-label' + inline,
19880             html : this.fieldLabel,
19881             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19882         };
19883         
19884  
19885         
19886         
19887         var input =  {
19888             tag: 'input',
19889             id : id,
19890             type : this.inputType,
19891             //value : (!this.checked) ? this.valueOff : this.inputValue,
19892             value : this.inputValue,
19893             cls : 'roo-radio',
19894             placeholder : this.placeholder || '' // ?? needed????
19895             
19896         };
19897         if (this.weight) { // Validity check?
19898             input.cls += " radio-" + this.weight;
19899         }
19900         if (this.disabled) {
19901             input.disabled=true;
19902         }
19903         
19904         if(this.checked){
19905             input.checked = this.checked;
19906         }
19907         
19908         if (this.name) {
19909             input.name = this.name;
19910         }
19911         
19912         if (this.size) {
19913             input.cls += ' input-' + this.size;
19914         }
19915         
19916         //?? can span's inline have a width??
19917         
19918         var settings=this;
19919         ['xs','sm','md','lg'].map(function(size){
19920             if (settings[size]) {
19921                 cfg.cls += ' col-' + size + '-' + settings[size];
19922             }
19923         });
19924         
19925         var inputblock = input;
19926         
19927         if (this.before || this.after) {
19928             
19929             inputblock = {
19930                 cls : 'input-group',
19931                 tag : 'span',
19932                 cn :  [] 
19933             };
19934             if (this.before) {
19935                 inputblock.cn.push({
19936                     tag :'span',
19937                     cls : 'input-group-addon',
19938                     html : this.before
19939                 });
19940             }
19941             inputblock.cn.push(input);
19942             if (this.after) {
19943                 inputblock.cn.push({
19944                     tag :'span',
19945                     cls : 'input-group-addon',
19946                     html : this.after
19947                 });
19948             }
19949             
19950         };
19951         
19952         
19953         if (this.fieldLabel && this.fieldLabel.length) {
19954             cfg.cn.push(fieldLabel);
19955         }
19956        
19957         // normal bootstrap puts the input inside the label.
19958         // however with our styled version - it has to go after the input.
19959        
19960         //lbl.cn.push(inputblock);
19961         
19962         var lblwrap =  {
19963             tag: 'span',
19964             cls: 'radio' + inline,
19965             cn: [
19966                 inputblock,
19967                 lbl
19968             ]
19969         };
19970         
19971         cfg.cn.push( lblwrap);
19972         
19973         if(this.boxLabel){
19974             lbl.cn.push({
19975                 tag: 'span',
19976                 html: this.boxLabel
19977             })
19978         }
19979          
19980         
19981         return cfg;
19982         
19983     },
19984     
19985     initEvents : function()
19986     {
19987 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19988         
19989         this.inputEl().on('click', this.onClick,  this);
19990         if (this.boxLabel) {
19991             //Roo.log('find label');
19992             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19993         }
19994         
19995     },
19996     
19997     inputEl: function ()
19998     {
19999         return this.el.select('input.roo-radio',true).first();
20000     },
20001     onClick : function()
20002     {   
20003         Roo.log("click");
20004         this.setChecked(true);
20005     },
20006     
20007     setChecked : function(state,suppressEvent)
20008     {
20009         if(state){
20010             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20011                 v.dom.checked = false;
20012             });
20013         }
20014         Roo.log(this.inputEl().dom);
20015         this.checked = state;
20016         this.inputEl().dom.checked = state;
20017         
20018         if(suppressEvent !== true){
20019             this.fireEvent('check', this, state);
20020         }
20021         
20022         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20023         
20024     },
20025     
20026     getGroupValue : function()
20027     {
20028         var value = '';
20029         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20030             if(v.dom.checked == true){
20031                 value = v.dom.value;
20032             }
20033         });
20034         
20035         return value;
20036     },
20037     
20038     /**
20039      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20040      * @return {Mixed} value The field value
20041      */
20042     getValue : function(){
20043         return this.getGroupValue();
20044     }
20045     
20046 });
20047
20048  
20049 //<script type="text/javascript">
20050
20051 /*
20052  * Based  Ext JS Library 1.1.1
20053  * Copyright(c) 2006-2007, Ext JS, LLC.
20054  * LGPL
20055  *
20056  */
20057  
20058 /**
20059  * @class Roo.HtmlEditorCore
20060  * @extends Roo.Component
20061  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20062  *
20063  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20064  */
20065
20066 Roo.HtmlEditorCore = function(config){
20067     
20068     
20069     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20070     
20071     
20072     this.addEvents({
20073         /**
20074          * @event initialize
20075          * Fires when the editor is fully initialized (including the iframe)
20076          * @param {Roo.HtmlEditorCore} this
20077          */
20078         initialize: true,
20079         /**
20080          * @event activate
20081          * Fires when the editor is first receives the focus. Any insertion must wait
20082          * until after this event.
20083          * @param {Roo.HtmlEditorCore} this
20084          */
20085         activate: true,
20086          /**
20087          * @event beforesync
20088          * Fires before the textarea is updated with content from the editor iframe. Return false
20089          * to cancel the sync.
20090          * @param {Roo.HtmlEditorCore} this
20091          * @param {String} html
20092          */
20093         beforesync: true,
20094          /**
20095          * @event beforepush
20096          * Fires before the iframe editor is updated with content from the textarea. Return false
20097          * to cancel the push.
20098          * @param {Roo.HtmlEditorCore} this
20099          * @param {String} html
20100          */
20101         beforepush: true,
20102          /**
20103          * @event sync
20104          * Fires when the textarea is updated with content from the editor iframe.
20105          * @param {Roo.HtmlEditorCore} this
20106          * @param {String} html
20107          */
20108         sync: true,
20109          /**
20110          * @event push
20111          * Fires when the iframe editor is updated with content from the textarea.
20112          * @param {Roo.HtmlEditorCore} this
20113          * @param {String} html
20114          */
20115         push: true,
20116         
20117         /**
20118          * @event editorevent
20119          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20120          * @param {Roo.HtmlEditorCore} this
20121          */
20122         editorevent: true
20123         
20124     });
20125     
20126     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20127     
20128     // defaults : white / black...
20129     this.applyBlacklists();
20130     
20131     
20132     
20133 };
20134
20135
20136 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20137
20138
20139      /**
20140      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20141      */
20142     
20143     owner : false,
20144     
20145      /**
20146      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20147      *                        Roo.resizable.
20148      */
20149     resizable : false,
20150      /**
20151      * @cfg {Number} height (in pixels)
20152      */   
20153     height: 300,
20154    /**
20155      * @cfg {Number} width (in pixels)
20156      */   
20157     width: 500,
20158     
20159     /**
20160      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20161      * 
20162      */
20163     stylesheets: false,
20164     
20165     // id of frame..
20166     frameId: false,
20167     
20168     // private properties
20169     validationEvent : false,
20170     deferHeight: true,
20171     initialized : false,
20172     activated : false,
20173     sourceEditMode : false,
20174     onFocus : Roo.emptyFn,
20175     iframePad:3,
20176     hideMode:'offsets',
20177     
20178     clearUp: true,
20179     
20180     // blacklist + whitelisted elements..
20181     black: false,
20182     white: false,
20183      
20184     
20185
20186     /**
20187      * Protected method that will not generally be called directly. It
20188      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20189      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20190      */
20191     getDocMarkup : function(){
20192         // body styles..
20193         var st = '';
20194         
20195         // inherit styels from page...?? 
20196         if (this.stylesheets === false) {
20197             
20198             Roo.get(document.head).select('style').each(function(node) {
20199                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20200             });
20201             
20202             Roo.get(document.head).select('link').each(function(node) { 
20203                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20204             });
20205             
20206         } else if (!this.stylesheets.length) {
20207                 // simple..
20208                 st = '<style type="text/css">' +
20209                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20210                    '</style>';
20211         } else { 
20212             
20213         }
20214         
20215         st +=  '<style type="text/css">' +
20216             'IMG { cursor: pointer } ' +
20217         '</style>';
20218
20219         
20220         return '<html><head>' + st  +
20221             //<style type="text/css">' +
20222             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20223             //'</style>' +
20224             ' </head><body class="roo-htmleditor-body"></body></html>';
20225     },
20226
20227     // private
20228     onRender : function(ct, position)
20229     {
20230         var _t = this;
20231         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20232         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20233         
20234         
20235         this.el.dom.style.border = '0 none';
20236         this.el.dom.setAttribute('tabIndex', -1);
20237         this.el.addClass('x-hidden hide');
20238         
20239         
20240         
20241         if(Roo.isIE){ // fix IE 1px bogus margin
20242             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20243         }
20244        
20245         
20246         this.frameId = Roo.id();
20247         
20248          
20249         
20250         var iframe = this.owner.wrap.createChild({
20251             tag: 'iframe',
20252             cls: 'form-control', // bootstrap..
20253             id: this.frameId,
20254             name: this.frameId,
20255             frameBorder : 'no',
20256             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20257         }, this.el
20258         );
20259         
20260         
20261         this.iframe = iframe.dom;
20262
20263          this.assignDocWin();
20264         
20265         this.doc.designMode = 'on';
20266        
20267         this.doc.open();
20268         this.doc.write(this.getDocMarkup());
20269         this.doc.close();
20270
20271         
20272         var task = { // must defer to wait for browser to be ready
20273             run : function(){
20274                 //console.log("run task?" + this.doc.readyState);
20275                 this.assignDocWin();
20276                 if(this.doc.body || this.doc.readyState == 'complete'){
20277                     try {
20278                         this.doc.designMode="on";
20279                     } catch (e) {
20280                         return;
20281                     }
20282                     Roo.TaskMgr.stop(task);
20283                     this.initEditor.defer(10, this);
20284                 }
20285             },
20286             interval : 10,
20287             duration: 10000,
20288             scope: this
20289         };
20290         Roo.TaskMgr.start(task);
20291
20292     },
20293
20294     // private
20295     onResize : function(w, h)
20296     {
20297          Roo.log('resize: ' +w + ',' + h );
20298         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20299         if(!this.iframe){
20300             return;
20301         }
20302         if(typeof w == 'number'){
20303             
20304             this.iframe.style.width = w + 'px';
20305         }
20306         if(typeof h == 'number'){
20307             
20308             this.iframe.style.height = h + 'px';
20309             if(this.doc){
20310                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20311             }
20312         }
20313         
20314     },
20315
20316     /**
20317      * Toggles the editor between standard and source edit mode.
20318      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20319      */
20320     toggleSourceEdit : function(sourceEditMode){
20321         
20322         this.sourceEditMode = sourceEditMode === true;
20323         
20324         if(this.sourceEditMode){
20325  
20326             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20327             
20328         }else{
20329             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20330             //this.iframe.className = '';
20331             this.deferFocus();
20332         }
20333         //this.setSize(this.owner.wrap.getSize());
20334         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20335     },
20336
20337     
20338   
20339
20340     /**
20341      * Protected method that will not generally be called directly. If you need/want
20342      * custom HTML cleanup, this is the method you should override.
20343      * @param {String} html The HTML to be cleaned
20344      * return {String} The cleaned HTML
20345      */
20346     cleanHtml : function(html){
20347         html = String(html);
20348         if(html.length > 5){
20349             if(Roo.isSafari){ // strip safari nonsense
20350                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20351             }
20352         }
20353         if(html == '&nbsp;'){
20354             html = '';
20355         }
20356         return html;
20357     },
20358
20359     /**
20360      * HTML Editor -> Textarea
20361      * Protected method that will not generally be called directly. Syncs the contents
20362      * of the editor iframe with the textarea.
20363      */
20364     syncValue : function(){
20365         if(this.initialized){
20366             var bd = (this.doc.body || this.doc.documentElement);
20367             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20368             var html = bd.innerHTML;
20369             if(Roo.isSafari){
20370                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20371                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20372                 if(m && m[1]){
20373                     html = '<div style="'+m[0]+'">' + html + '</div>';
20374                 }
20375             }
20376             html = this.cleanHtml(html);
20377             // fix up the special chars.. normaly like back quotes in word...
20378             // however we do not want to do this with chinese..
20379             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20380                 var cc = b.charCodeAt();
20381                 if (
20382                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20383                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20384                     (cc >= 0xf900 && cc < 0xfb00 )
20385                 ) {
20386                         return b;
20387                 }
20388                 return "&#"+cc+";" 
20389             });
20390             if(this.owner.fireEvent('beforesync', this, html) !== false){
20391                 this.el.dom.value = html;
20392                 this.owner.fireEvent('sync', this, html);
20393             }
20394         }
20395     },
20396
20397     /**
20398      * Protected method that will not generally be called directly. Pushes the value of the textarea
20399      * into the iframe editor.
20400      */
20401     pushValue : function(){
20402         if(this.initialized){
20403             var v = this.el.dom.value.trim();
20404             
20405 //            if(v.length < 1){
20406 //                v = '&#160;';
20407 //            }
20408             
20409             if(this.owner.fireEvent('beforepush', this, v) !== false){
20410                 var d = (this.doc.body || this.doc.documentElement);
20411                 d.innerHTML = v;
20412                 this.cleanUpPaste();
20413                 this.el.dom.value = d.innerHTML;
20414                 this.owner.fireEvent('push', this, v);
20415             }
20416         }
20417     },
20418
20419     // private
20420     deferFocus : function(){
20421         this.focus.defer(10, this);
20422     },
20423
20424     // doc'ed in Field
20425     focus : function(){
20426         if(this.win && !this.sourceEditMode){
20427             this.win.focus();
20428         }else{
20429             this.el.focus();
20430         }
20431     },
20432     
20433     assignDocWin: function()
20434     {
20435         var iframe = this.iframe;
20436         
20437          if(Roo.isIE){
20438             this.doc = iframe.contentWindow.document;
20439             this.win = iframe.contentWindow;
20440         } else {
20441 //            if (!Roo.get(this.frameId)) {
20442 //                return;
20443 //            }
20444 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20445 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20446             
20447             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20448                 return;
20449             }
20450             
20451             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20452             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20453         }
20454     },
20455     
20456     // private
20457     initEditor : function(){
20458         //console.log("INIT EDITOR");
20459         this.assignDocWin();
20460         
20461         
20462         
20463         this.doc.designMode="on";
20464         this.doc.open();
20465         this.doc.write(this.getDocMarkup());
20466         this.doc.close();
20467         
20468         var dbody = (this.doc.body || this.doc.documentElement);
20469         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20470         // this copies styles from the containing element into thsi one..
20471         // not sure why we need all of this..
20472         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20473         
20474         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20475         //ss['background-attachment'] = 'fixed'; // w3c
20476         dbody.bgProperties = 'fixed'; // ie
20477         //Roo.DomHelper.applyStyles(dbody, ss);
20478         Roo.EventManager.on(this.doc, {
20479             //'mousedown': this.onEditorEvent,
20480             'mouseup': this.onEditorEvent,
20481             'dblclick': this.onEditorEvent,
20482             'click': this.onEditorEvent,
20483             'keyup': this.onEditorEvent,
20484             buffer:100,
20485             scope: this
20486         });
20487         if(Roo.isGecko){
20488             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20489         }
20490         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20491             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20492         }
20493         this.initialized = true;
20494
20495         this.owner.fireEvent('initialize', this);
20496         this.pushValue();
20497     },
20498
20499     // private
20500     onDestroy : function(){
20501         
20502         
20503         
20504         if(this.rendered){
20505             
20506             //for (var i =0; i < this.toolbars.length;i++) {
20507             //    // fixme - ask toolbars for heights?
20508             //    this.toolbars[i].onDestroy();
20509            // }
20510             
20511             //this.wrap.dom.innerHTML = '';
20512             //this.wrap.remove();
20513         }
20514     },
20515
20516     // private
20517     onFirstFocus : function(){
20518         
20519         this.assignDocWin();
20520         
20521         
20522         this.activated = true;
20523          
20524     
20525         if(Roo.isGecko){ // prevent silly gecko errors
20526             this.win.focus();
20527             var s = this.win.getSelection();
20528             if(!s.focusNode || s.focusNode.nodeType != 3){
20529                 var r = s.getRangeAt(0);
20530                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20531                 r.collapse(true);
20532                 this.deferFocus();
20533             }
20534             try{
20535                 this.execCmd('useCSS', true);
20536                 this.execCmd('styleWithCSS', false);
20537             }catch(e){}
20538         }
20539         this.owner.fireEvent('activate', this);
20540     },
20541
20542     // private
20543     adjustFont: function(btn){
20544         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20545         //if(Roo.isSafari){ // safari
20546         //    adjust *= 2;
20547        // }
20548         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20549         if(Roo.isSafari){ // safari
20550             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20551             v =  (v < 10) ? 10 : v;
20552             v =  (v > 48) ? 48 : v;
20553             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20554             
20555         }
20556         
20557         
20558         v = Math.max(1, v+adjust);
20559         
20560         this.execCmd('FontSize', v  );
20561     },
20562
20563     onEditorEvent : function(e)
20564     {
20565         this.owner.fireEvent('editorevent', this, e);
20566       //  this.updateToolbar();
20567         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20568     },
20569
20570     insertTag : function(tg)
20571     {
20572         // could be a bit smarter... -> wrap the current selected tRoo..
20573         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20574             
20575             range = this.createRange(this.getSelection());
20576             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20577             wrappingNode.appendChild(range.extractContents());
20578             range.insertNode(wrappingNode);
20579
20580             return;
20581             
20582             
20583             
20584         }
20585         this.execCmd("formatblock",   tg);
20586         
20587     },
20588     
20589     insertText : function(txt)
20590     {
20591         
20592         
20593         var range = this.createRange();
20594         range.deleteContents();
20595                //alert(Sender.getAttribute('label'));
20596                
20597         range.insertNode(this.doc.createTextNode(txt));
20598     } ,
20599     
20600      
20601
20602     /**
20603      * Executes a Midas editor command on the editor document and performs necessary focus and
20604      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20605      * @param {String} cmd The Midas command
20606      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20607      */
20608     relayCmd : function(cmd, value){
20609         this.win.focus();
20610         this.execCmd(cmd, value);
20611         this.owner.fireEvent('editorevent', this);
20612         //this.updateToolbar();
20613         this.owner.deferFocus();
20614     },
20615
20616     /**
20617      * Executes a Midas editor command directly on the editor document.
20618      * For visual commands, you should use {@link #relayCmd} instead.
20619      * <b>This should only be called after the editor is initialized.</b>
20620      * @param {String} cmd The Midas command
20621      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20622      */
20623     execCmd : function(cmd, value){
20624         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20625         this.syncValue();
20626     },
20627  
20628  
20629    
20630     /**
20631      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20632      * to insert tRoo.
20633      * @param {String} text | dom node.. 
20634      */
20635     insertAtCursor : function(text)
20636     {
20637         
20638         
20639         
20640         if(!this.activated){
20641             return;
20642         }
20643         /*
20644         if(Roo.isIE){
20645             this.win.focus();
20646             var r = this.doc.selection.createRange();
20647             if(r){
20648                 r.collapse(true);
20649                 r.pasteHTML(text);
20650                 this.syncValue();
20651                 this.deferFocus();
20652             
20653             }
20654             return;
20655         }
20656         */
20657         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20658             this.win.focus();
20659             
20660             
20661             // from jquery ui (MIT licenced)
20662             var range, node;
20663             var win = this.win;
20664             
20665             if (win.getSelection && win.getSelection().getRangeAt) {
20666                 range = win.getSelection().getRangeAt(0);
20667                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20668                 range.insertNode(node);
20669             } else if (win.document.selection && win.document.selection.createRange) {
20670                 // no firefox support
20671                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20672                 win.document.selection.createRange().pasteHTML(txt);
20673             } else {
20674                 // no firefox support
20675                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20676                 this.execCmd('InsertHTML', txt);
20677             } 
20678             
20679             this.syncValue();
20680             
20681             this.deferFocus();
20682         }
20683     },
20684  // private
20685     mozKeyPress : function(e){
20686         if(e.ctrlKey){
20687             var c = e.getCharCode(), cmd;
20688           
20689             if(c > 0){
20690                 c = String.fromCharCode(c).toLowerCase();
20691                 switch(c){
20692                     case 'b':
20693                         cmd = 'bold';
20694                         break;
20695                     case 'i':
20696                         cmd = 'italic';
20697                         break;
20698                     
20699                     case 'u':
20700                         cmd = 'underline';
20701                         break;
20702                     
20703                     case 'v':
20704                         this.cleanUpPaste.defer(100, this);
20705                         return;
20706                         
20707                 }
20708                 if(cmd){
20709                     this.win.focus();
20710                     this.execCmd(cmd);
20711                     this.deferFocus();
20712                     e.preventDefault();
20713                 }
20714                 
20715             }
20716         }
20717     },
20718
20719     // private
20720     fixKeys : function(){ // load time branching for fastest keydown performance
20721         if(Roo.isIE){
20722             return function(e){
20723                 var k = e.getKey(), r;
20724                 if(k == e.TAB){
20725                     e.stopEvent();
20726                     r = this.doc.selection.createRange();
20727                     if(r){
20728                         r.collapse(true);
20729                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20730                         this.deferFocus();
20731                     }
20732                     return;
20733                 }
20734                 
20735                 if(k == e.ENTER){
20736                     r = this.doc.selection.createRange();
20737                     if(r){
20738                         var target = r.parentElement();
20739                         if(!target || target.tagName.toLowerCase() != 'li'){
20740                             e.stopEvent();
20741                             r.pasteHTML('<br />');
20742                             r.collapse(false);
20743                             r.select();
20744                         }
20745                     }
20746                 }
20747                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20748                     this.cleanUpPaste.defer(100, this);
20749                     return;
20750                 }
20751                 
20752                 
20753             };
20754         }else if(Roo.isOpera){
20755             return function(e){
20756                 var k = e.getKey();
20757                 if(k == e.TAB){
20758                     e.stopEvent();
20759                     this.win.focus();
20760                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20761                     this.deferFocus();
20762                 }
20763                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20764                     this.cleanUpPaste.defer(100, this);
20765                     return;
20766                 }
20767                 
20768             };
20769         }else if(Roo.isSafari){
20770             return function(e){
20771                 var k = e.getKey();
20772                 
20773                 if(k == e.TAB){
20774                     e.stopEvent();
20775                     this.execCmd('InsertText','\t');
20776                     this.deferFocus();
20777                     return;
20778                 }
20779                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20780                     this.cleanUpPaste.defer(100, this);
20781                     return;
20782                 }
20783                 
20784              };
20785         }
20786     }(),
20787     
20788     getAllAncestors: function()
20789     {
20790         var p = this.getSelectedNode();
20791         var a = [];
20792         if (!p) {
20793             a.push(p); // push blank onto stack..
20794             p = this.getParentElement();
20795         }
20796         
20797         
20798         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20799             a.push(p);
20800             p = p.parentNode;
20801         }
20802         a.push(this.doc.body);
20803         return a;
20804     },
20805     lastSel : false,
20806     lastSelNode : false,
20807     
20808     
20809     getSelection : function() 
20810     {
20811         this.assignDocWin();
20812         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20813     },
20814     
20815     getSelectedNode: function() 
20816     {
20817         // this may only work on Gecko!!!
20818         
20819         // should we cache this!!!!
20820         
20821         
20822         
20823          
20824         var range = this.createRange(this.getSelection()).cloneRange();
20825         
20826         if (Roo.isIE) {
20827             var parent = range.parentElement();
20828             while (true) {
20829                 var testRange = range.duplicate();
20830                 testRange.moveToElementText(parent);
20831                 if (testRange.inRange(range)) {
20832                     break;
20833                 }
20834                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20835                     break;
20836                 }
20837                 parent = parent.parentElement;
20838             }
20839             return parent;
20840         }
20841         
20842         // is ancestor a text element.
20843         var ac =  range.commonAncestorContainer;
20844         if (ac.nodeType == 3) {
20845             ac = ac.parentNode;
20846         }
20847         
20848         var ar = ac.childNodes;
20849          
20850         var nodes = [];
20851         var other_nodes = [];
20852         var has_other_nodes = false;
20853         for (var i=0;i<ar.length;i++) {
20854             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20855                 continue;
20856             }
20857             // fullly contained node.
20858             
20859             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20860                 nodes.push(ar[i]);
20861                 continue;
20862             }
20863             
20864             // probably selected..
20865             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20866                 other_nodes.push(ar[i]);
20867                 continue;
20868             }
20869             // outer..
20870             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20871                 continue;
20872             }
20873             
20874             
20875             has_other_nodes = true;
20876         }
20877         if (!nodes.length && other_nodes.length) {
20878             nodes= other_nodes;
20879         }
20880         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20881             return false;
20882         }
20883         
20884         return nodes[0];
20885     },
20886     createRange: function(sel)
20887     {
20888         // this has strange effects when using with 
20889         // top toolbar - not sure if it's a great idea.
20890         //this.editor.contentWindow.focus();
20891         if (typeof sel != "undefined") {
20892             try {
20893                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20894             } catch(e) {
20895                 return this.doc.createRange();
20896             }
20897         } else {
20898             return this.doc.createRange();
20899         }
20900     },
20901     getParentElement: function()
20902     {
20903         
20904         this.assignDocWin();
20905         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20906         
20907         var range = this.createRange(sel);
20908          
20909         try {
20910             var p = range.commonAncestorContainer;
20911             while (p.nodeType == 3) { // text node
20912                 p = p.parentNode;
20913             }
20914             return p;
20915         } catch (e) {
20916             return null;
20917         }
20918     
20919     },
20920     /***
20921      *
20922      * Range intersection.. the hard stuff...
20923      *  '-1' = before
20924      *  '0' = hits..
20925      *  '1' = after.
20926      *         [ -- selected range --- ]
20927      *   [fail]                        [fail]
20928      *
20929      *    basically..
20930      *      if end is before start or  hits it. fail.
20931      *      if start is after end or hits it fail.
20932      *
20933      *   if either hits (but other is outside. - then it's not 
20934      *   
20935      *    
20936      **/
20937     
20938     
20939     // @see http://www.thismuchiknow.co.uk/?p=64.
20940     rangeIntersectsNode : function(range, node)
20941     {
20942         var nodeRange = node.ownerDocument.createRange();
20943         try {
20944             nodeRange.selectNode(node);
20945         } catch (e) {
20946             nodeRange.selectNodeContents(node);
20947         }
20948     
20949         var rangeStartRange = range.cloneRange();
20950         rangeStartRange.collapse(true);
20951     
20952         var rangeEndRange = range.cloneRange();
20953         rangeEndRange.collapse(false);
20954     
20955         var nodeStartRange = nodeRange.cloneRange();
20956         nodeStartRange.collapse(true);
20957     
20958         var nodeEndRange = nodeRange.cloneRange();
20959         nodeEndRange.collapse(false);
20960     
20961         return rangeStartRange.compareBoundaryPoints(
20962                  Range.START_TO_START, nodeEndRange) == -1 &&
20963                rangeEndRange.compareBoundaryPoints(
20964                  Range.START_TO_START, nodeStartRange) == 1;
20965         
20966          
20967     },
20968     rangeCompareNode : function(range, node)
20969     {
20970         var nodeRange = node.ownerDocument.createRange();
20971         try {
20972             nodeRange.selectNode(node);
20973         } catch (e) {
20974             nodeRange.selectNodeContents(node);
20975         }
20976         
20977         
20978         range.collapse(true);
20979     
20980         nodeRange.collapse(true);
20981      
20982         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20983         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20984          
20985         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20986         
20987         var nodeIsBefore   =  ss == 1;
20988         var nodeIsAfter    = ee == -1;
20989         
20990         if (nodeIsBefore && nodeIsAfter) {
20991             return 0; // outer
20992         }
20993         if (!nodeIsBefore && nodeIsAfter) {
20994             return 1; //right trailed.
20995         }
20996         
20997         if (nodeIsBefore && !nodeIsAfter) {
20998             return 2;  // left trailed.
20999         }
21000         // fully contined.
21001         return 3;
21002     },
21003
21004     // private? - in a new class?
21005     cleanUpPaste :  function()
21006     {
21007         // cleans up the whole document..
21008         Roo.log('cleanuppaste');
21009         
21010         this.cleanUpChildren(this.doc.body);
21011         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21012         if (clean != this.doc.body.innerHTML) {
21013             this.doc.body.innerHTML = clean;
21014         }
21015         
21016     },
21017     
21018     cleanWordChars : function(input) {// change the chars to hex code
21019         var he = Roo.HtmlEditorCore;
21020         
21021         var output = input;
21022         Roo.each(he.swapCodes, function(sw) { 
21023             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21024             
21025             output = output.replace(swapper, sw[1]);
21026         });
21027         
21028         return output;
21029     },
21030     
21031     
21032     cleanUpChildren : function (n)
21033     {
21034         if (!n.childNodes.length) {
21035             return;
21036         }
21037         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21038            this.cleanUpChild(n.childNodes[i]);
21039         }
21040     },
21041     
21042     
21043         
21044     
21045     cleanUpChild : function (node)
21046     {
21047         var ed = this;
21048         //console.log(node);
21049         if (node.nodeName == "#text") {
21050             // clean up silly Windows -- stuff?
21051             return; 
21052         }
21053         if (node.nodeName == "#comment") {
21054             node.parentNode.removeChild(node);
21055             // clean up silly Windows -- stuff?
21056             return; 
21057         }
21058         var lcname = node.tagName.toLowerCase();
21059         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21060         // whitelist of tags..
21061         
21062         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21063             // remove node.
21064             node.parentNode.removeChild(node);
21065             return;
21066             
21067         }
21068         
21069         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21070         
21071         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21072         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21073         
21074         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21075         //    remove_keep_children = true;
21076         //}
21077         
21078         if (remove_keep_children) {
21079             this.cleanUpChildren(node);
21080             // inserts everything just before this node...
21081             while (node.childNodes.length) {
21082                 var cn = node.childNodes[0];
21083                 node.removeChild(cn);
21084                 node.parentNode.insertBefore(cn, node);
21085             }
21086             node.parentNode.removeChild(node);
21087             return;
21088         }
21089         
21090         if (!node.attributes || !node.attributes.length) {
21091             this.cleanUpChildren(node);
21092             return;
21093         }
21094         
21095         function cleanAttr(n,v)
21096         {
21097             
21098             if (v.match(/^\./) || v.match(/^\//)) {
21099                 return;
21100             }
21101             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21102                 return;
21103             }
21104             if (v.match(/^#/)) {
21105                 return;
21106             }
21107 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21108             node.removeAttribute(n);
21109             
21110         }
21111         
21112         var cwhite = this.cwhite;
21113         var cblack = this.cblack;
21114             
21115         function cleanStyle(n,v)
21116         {
21117             if (v.match(/expression/)) { //XSS?? should we even bother..
21118                 node.removeAttribute(n);
21119                 return;
21120             }
21121             
21122             var parts = v.split(/;/);
21123             var clean = [];
21124             
21125             Roo.each(parts, function(p) {
21126                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21127                 if (!p.length) {
21128                     return true;
21129                 }
21130                 var l = p.split(':').shift().replace(/\s+/g,'');
21131                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21132                 
21133                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21134 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21135                     //node.removeAttribute(n);
21136                     return true;
21137                 }
21138                 //Roo.log()
21139                 // only allow 'c whitelisted system attributes'
21140                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21141 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21142                     //node.removeAttribute(n);
21143                     return true;
21144                 }
21145                 
21146                 
21147                  
21148                 
21149                 clean.push(p);
21150                 return true;
21151             });
21152             if (clean.length) { 
21153                 node.setAttribute(n, clean.join(';'));
21154             } else {
21155                 node.removeAttribute(n);
21156             }
21157             
21158         }
21159         
21160         
21161         for (var i = node.attributes.length-1; i > -1 ; i--) {
21162             var a = node.attributes[i];
21163             //console.log(a);
21164             
21165             if (a.name.toLowerCase().substr(0,2)=='on')  {
21166                 node.removeAttribute(a.name);
21167                 continue;
21168             }
21169             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21170                 node.removeAttribute(a.name);
21171                 continue;
21172             }
21173             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21174                 cleanAttr(a.name,a.value); // fixme..
21175                 continue;
21176             }
21177             if (a.name == 'style') {
21178                 cleanStyle(a.name,a.value);
21179                 continue;
21180             }
21181             /// clean up MS crap..
21182             // tecnically this should be a list of valid class'es..
21183             
21184             
21185             if (a.name == 'class') {
21186                 if (a.value.match(/^Mso/)) {
21187                     node.className = '';
21188                 }
21189                 
21190                 if (a.value.match(/body/)) {
21191                     node.className = '';
21192                 }
21193                 continue;
21194             }
21195             
21196             // style cleanup!?
21197             // class cleanup?
21198             
21199         }
21200         
21201         
21202         this.cleanUpChildren(node);
21203         
21204         
21205     },
21206     
21207     /**
21208      * Clean up MS wordisms...
21209      */
21210     cleanWord : function(node)
21211     {
21212         
21213         
21214         if (!node) {
21215             this.cleanWord(this.doc.body);
21216             return;
21217         }
21218         if (node.nodeName == "#text") {
21219             // clean up silly Windows -- stuff?
21220             return; 
21221         }
21222         if (node.nodeName == "#comment") {
21223             node.parentNode.removeChild(node);
21224             // clean up silly Windows -- stuff?
21225             return; 
21226         }
21227         
21228         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21229             node.parentNode.removeChild(node);
21230             return;
21231         }
21232         
21233         // remove - but keep children..
21234         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21235             while (node.childNodes.length) {
21236                 var cn = node.childNodes[0];
21237                 node.removeChild(cn);
21238                 node.parentNode.insertBefore(cn, node);
21239             }
21240             node.parentNode.removeChild(node);
21241             this.iterateChildren(node, this.cleanWord);
21242             return;
21243         }
21244         // clean styles
21245         if (node.className.length) {
21246             
21247             var cn = node.className.split(/\W+/);
21248             var cna = [];
21249             Roo.each(cn, function(cls) {
21250                 if (cls.match(/Mso[a-zA-Z]+/)) {
21251                     return;
21252                 }
21253                 cna.push(cls);
21254             });
21255             node.className = cna.length ? cna.join(' ') : '';
21256             if (!cna.length) {
21257                 node.removeAttribute("class");
21258             }
21259         }
21260         
21261         if (node.hasAttribute("lang")) {
21262             node.removeAttribute("lang");
21263         }
21264         
21265         if (node.hasAttribute("style")) {
21266             
21267             var styles = node.getAttribute("style").split(";");
21268             var nstyle = [];
21269             Roo.each(styles, function(s) {
21270                 if (!s.match(/:/)) {
21271                     return;
21272                 }
21273                 var kv = s.split(":");
21274                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21275                     return;
21276                 }
21277                 // what ever is left... we allow.
21278                 nstyle.push(s);
21279             });
21280             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21281             if (!nstyle.length) {
21282                 node.removeAttribute('style');
21283             }
21284         }
21285         this.iterateChildren(node, this.cleanWord);
21286         
21287         
21288         
21289     },
21290     /**
21291      * iterateChildren of a Node, calling fn each time, using this as the scole..
21292      * @param {DomNode} node node to iterate children of.
21293      * @param {Function} fn method of this class to call on each item.
21294      */
21295     iterateChildren : function(node, fn)
21296     {
21297         if (!node.childNodes.length) {
21298                 return;
21299         }
21300         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21301            fn.call(this, node.childNodes[i])
21302         }
21303     },
21304     
21305     
21306     /**
21307      * cleanTableWidths.
21308      *
21309      * Quite often pasting from word etc.. results in tables with column and widths.
21310      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21311      *
21312      */
21313     cleanTableWidths : function(node)
21314     {
21315          
21316          
21317         if (!node) {
21318             this.cleanTableWidths(this.doc.body);
21319             return;
21320         }
21321         
21322         // ignore list...
21323         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21324             return; 
21325         }
21326         Roo.log(node.tagName);
21327         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21328             this.iterateChildren(node, this.cleanTableWidths);
21329             return;
21330         }
21331         if (node.hasAttribute('width')) {
21332             node.removeAttribute('width');
21333         }
21334         
21335          
21336         if (node.hasAttribute("style")) {
21337             // pretty basic...
21338             
21339             var styles = node.getAttribute("style").split(";");
21340             var nstyle = [];
21341             Roo.each(styles, function(s) {
21342                 if (!s.match(/:/)) {
21343                     return;
21344                 }
21345                 var kv = s.split(":");
21346                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21347                     return;
21348                 }
21349                 // what ever is left... we allow.
21350                 nstyle.push(s);
21351             });
21352             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21353             if (!nstyle.length) {
21354                 node.removeAttribute('style');
21355             }
21356         }
21357         
21358         this.iterateChildren(node, this.cleanTableWidths);
21359         
21360         
21361     },
21362     
21363     
21364     
21365     
21366     domToHTML : function(currentElement, depth, nopadtext) {
21367         
21368         depth = depth || 0;
21369         nopadtext = nopadtext || false;
21370     
21371         if (!currentElement) {
21372             return this.domToHTML(this.doc.body);
21373         }
21374         
21375         //Roo.log(currentElement);
21376         var j;
21377         var allText = false;
21378         var nodeName = currentElement.nodeName;
21379         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21380         
21381         if  (nodeName == '#text') {
21382             
21383             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21384         }
21385         
21386         
21387         var ret = '';
21388         if (nodeName != 'BODY') {
21389              
21390             var i = 0;
21391             // Prints the node tagName, such as <A>, <IMG>, etc
21392             if (tagName) {
21393                 var attr = [];
21394                 for(i = 0; i < currentElement.attributes.length;i++) {
21395                     // quoting?
21396                     var aname = currentElement.attributes.item(i).name;
21397                     if (!currentElement.attributes.item(i).value.length) {
21398                         continue;
21399                     }
21400                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21401                 }
21402                 
21403                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21404             } 
21405             else {
21406                 
21407                 // eack
21408             }
21409         } else {
21410             tagName = false;
21411         }
21412         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21413             return ret;
21414         }
21415         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21416             nopadtext = true;
21417         }
21418         
21419         
21420         // Traverse the tree
21421         i = 0;
21422         var currentElementChild = currentElement.childNodes.item(i);
21423         var allText = true;
21424         var innerHTML  = '';
21425         lastnode = '';
21426         while (currentElementChild) {
21427             // Formatting code (indent the tree so it looks nice on the screen)
21428             var nopad = nopadtext;
21429             if (lastnode == 'SPAN') {
21430                 nopad  = true;
21431             }
21432             // text
21433             if  (currentElementChild.nodeName == '#text') {
21434                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21435                 toadd = nopadtext ? toadd : toadd.trim();
21436                 if (!nopad && toadd.length > 80) {
21437                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21438                 }
21439                 innerHTML  += toadd;
21440                 
21441                 i++;
21442                 currentElementChild = currentElement.childNodes.item(i);
21443                 lastNode = '';
21444                 continue;
21445             }
21446             allText = false;
21447             
21448             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21449                 
21450             // Recursively traverse the tree structure of the child node
21451             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21452             lastnode = currentElementChild.nodeName;
21453             i++;
21454             currentElementChild=currentElement.childNodes.item(i);
21455         }
21456         
21457         ret += innerHTML;
21458         
21459         if (!allText) {
21460                 // The remaining code is mostly for formatting the tree
21461             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21462         }
21463         
21464         
21465         if (tagName) {
21466             ret+= "</"+tagName+">";
21467         }
21468         return ret;
21469         
21470     },
21471         
21472     applyBlacklists : function()
21473     {
21474         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21475         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21476         
21477         this.white = [];
21478         this.black = [];
21479         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21480             if (b.indexOf(tag) > -1) {
21481                 return;
21482             }
21483             this.white.push(tag);
21484             
21485         }, this);
21486         
21487         Roo.each(w, function(tag) {
21488             if (b.indexOf(tag) > -1) {
21489                 return;
21490             }
21491             if (this.white.indexOf(tag) > -1) {
21492                 return;
21493             }
21494             this.white.push(tag);
21495             
21496         }, this);
21497         
21498         
21499         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21500             if (w.indexOf(tag) > -1) {
21501                 return;
21502             }
21503             this.black.push(tag);
21504             
21505         }, this);
21506         
21507         Roo.each(b, function(tag) {
21508             if (w.indexOf(tag) > -1) {
21509                 return;
21510             }
21511             if (this.black.indexOf(tag) > -1) {
21512                 return;
21513             }
21514             this.black.push(tag);
21515             
21516         }, this);
21517         
21518         
21519         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21520         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21521         
21522         this.cwhite = [];
21523         this.cblack = [];
21524         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21525             if (b.indexOf(tag) > -1) {
21526                 return;
21527             }
21528             this.cwhite.push(tag);
21529             
21530         }, this);
21531         
21532         Roo.each(w, function(tag) {
21533             if (b.indexOf(tag) > -1) {
21534                 return;
21535             }
21536             if (this.cwhite.indexOf(tag) > -1) {
21537                 return;
21538             }
21539             this.cwhite.push(tag);
21540             
21541         }, this);
21542         
21543         
21544         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21545             if (w.indexOf(tag) > -1) {
21546                 return;
21547             }
21548             this.cblack.push(tag);
21549             
21550         }, this);
21551         
21552         Roo.each(b, function(tag) {
21553             if (w.indexOf(tag) > -1) {
21554                 return;
21555             }
21556             if (this.cblack.indexOf(tag) > -1) {
21557                 return;
21558             }
21559             this.cblack.push(tag);
21560             
21561         }, this);
21562     },
21563     
21564     setStylesheets : function(stylesheets)
21565     {
21566         if(typeof(stylesheets) == 'string'){
21567             Roo.get(this.iframe.contentDocument.head).createChild({
21568                 tag : 'link',
21569                 rel : 'stylesheet',
21570                 type : 'text/css',
21571                 href : stylesheets
21572             });
21573             
21574             return;
21575         }
21576         var _this = this;
21577      
21578         Roo.each(stylesheets, function(s) {
21579             if(!s.length){
21580                 return;
21581             }
21582             
21583             Roo.get(_this.iframe.contentDocument.head).createChild({
21584                 tag : 'link',
21585                 rel : 'stylesheet',
21586                 type : 'text/css',
21587                 href : s
21588             });
21589         });
21590
21591         
21592     },
21593     
21594     removeStylesheets : function()
21595     {
21596         var _this = this;
21597         
21598         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21599             s.remove();
21600         });
21601     }
21602     
21603     // hide stuff that is not compatible
21604     /**
21605      * @event blur
21606      * @hide
21607      */
21608     /**
21609      * @event change
21610      * @hide
21611      */
21612     /**
21613      * @event focus
21614      * @hide
21615      */
21616     /**
21617      * @event specialkey
21618      * @hide
21619      */
21620     /**
21621      * @cfg {String} fieldClass @hide
21622      */
21623     /**
21624      * @cfg {String} focusClass @hide
21625      */
21626     /**
21627      * @cfg {String} autoCreate @hide
21628      */
21629     /**
21630      * @cfg {String} inputType @hide
21631      */
21632     /**
21633      * @cfg {String} invalidClass @hide
21634      */
21635     /**
21636      * @cfg {String} invalidText @hide
21637      */
21638     /**
21639      * @cfg {String} msgFx @hide
21640      */
21641     /**
21642      * @cfg {String} validateOnBlur @hide
21643      */
21644 });
21645
21646 Roo.HtmlEditorCore.white = [
21647         'area', 'br', 'img', 'input', 'hr', 'wbr',
21648         
21649        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21650        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21651        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21652        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21653        'table',   'ul',         'xmp', 
21654        
21655        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21656       'thead',   'tr', 
21657      
21658       'dir', 'menu', 'ol', 'ul', 'dl',
21659        
21660       'embed',  'object'
21661 ];
21662
21663
21664 Roo.HtmlEditorCore.black = [
21665     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21666         'applet', // 
21667         'base',   'basefont', 'bgsound', 'blink',  'body', 
21668         'frame',  'frameset', 'head',    'html',   'ilayer', 
21669         'iframe', 'layer',  'link',     'meta',    'object',   
21670         'script', 'style' ,'title',  'xml' // clean later..
21671 ];
21672 Roo.HtmlEditorCore.clean = [
21673     'script', 'style', 'title', 'xml'
21674 ];
21675 Roo.HtmlEditorCore.remove = [
21676     'font'
21677 ];
21678 // attributes..
21679
21680 Roo.HtmlEditorCore.ablack = [
21681     'on'
21682 ];
21683     
21684 Roo.HtmlEditorCore.aclean = [ 
21685     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21686 ];
21687
21688 // protocols..
21689 Roo.HtmlEditorCore.pwhite= [
21690         'http',  'https',  'mailto'
21691 ];
21692
21693 // white listed style attributes.
21694 Roo.HtmlEditorCore.cwhite= [
21695       //  'text-align', /// default is to allow most things..
21696       
21697          
21698 //        'font-size'//??
21699 ];
21700
21701 // black listed style attributes.
21702 Roo.HtmlEditorCore.cblack= [
21703       //  'font-size' -- this can be set by the project 
21704 ];
21705
21706
21707 Roo.HtmlEditorCore.swapCodes   =[ 
21708     [    8211, "--" ], 
21709     [    8212, "--" ], 
21710     [    8216,  "'" ],  
21711     [    8217, "'" ],  
21712     [    8220, '"' ],  
21713     [    8221, '"' ],  
21714     [    8226, "*" ],  
21715     [    8230, "..." ]
21716 ]; 
21717
21718     /*
21719  * - LGPL
21720  *
21721  * HtmlEditor
21722  * 
21723  */
21724
21725 /**
21726  * @class Roo.bootstrap.HtmlEditor
21727  * @extends Roo.bootstrap.TextArea
21728  * Bootstrap HtmlEditor class
21729
21730  * @constructor
21731  * Create a new HtmlEditor
21732  * @param {Object} config The config object
21733  */
21734
21735 Roo.bootstrap.HtmlEditor = function(config){
21736     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21737     if (!this.toolbars) {
21738         this.toolbars = [];
21739     }
21740     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21741     this.addEvents({
21742             /**
21743              * @event initialize
21744              * Fires when the editor is fully initialized (including the iframe)
21745              * @param {HtmlEditor} this
21746              */
21747             initialize: true,
21748             /**
21749              * @event activate
21750              * Fires when the editor is first receives the focus. Any insertion must wait
21751              * until after this event.
21752              * @param {HtmlEditor} this
21753              */
21754             activate: true,
21755              /**
21756              * @event beforesync
21757              * Fires before the textarea is updated with content from the editor iframe. Return false
21758              * to cancel the sync.
21759              * @param {HtmlEditor} this
21760              * @param {String} html
21761              */
21762             beforesync: true,
21763              /**
21764              * @event beforepush
21765              * Fires before the iframe editor is updated with content from the textarea. Return false
21766              * to cancel the push.
21767              * @param {HtmlEditor} this
21768              * @param {String} html
21769              */
21770             beforepush: true,
21771              /**
21772              * @event sync
21773              * Fires when the textarea is updated with content from the editor iframe.
21774              * @param {HtmlEditor} this
21775              * @param {String} html
21776              */
21777             sync: true,
21778              /**
21779              * @event push
21780              * Fires when the iframe editor is updated with content from the textarea.
21781              * @param {HtmlEditor} this
21782              * @param {String} html
21783              */
21784             push: true,
21785              /**
21786              * @event editmodechange
21787              * Fires when the editor switches edit modes
21788              * @param {HtmlEditor} this
21789              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21790              */
21791             editmodechange: true,
21792             /**
21793              * @event editorevent
21794              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21795              * @param {HtmlEditor} this
21796              */
21797             editorevent: true,
21798             /**
21799              * @event firstfocus
21800              * Fires when on first focus - needed by toolbars..
21801              * @param {HtmlEditor} this
21802              */
21803             firstfocus: true,
21804             /**
21805              * @event autosave
21806              * Auto save the htmlEditor value as a file into Events
21807              * @param {HtmlEditor} this
21808              */
21809             autosave: true,
21810             /**
21811              * @event savedpreview
21812              * preview the saved version of htmlEditor
21813              * @param {HtmlEditor} this
21814              */
21815             savedpreview: true
21816         });
21817 };
21818
21819
21820 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21821     
21822     
21823       /**
21824      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21825      */
21826     toolbars : false,
21827    
21828      /**
21829      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21830      *                        Roo.resizable.
21831      */
21832     resizable : false,
21833      /**
21834      * @cfg {Number} height (in pixels)
21835      */   
21836     height: 300,
21837    /**
21838      * @cfg {Number} width (in pixels)
21839      */   
21840     width: false,
21841     
21842     /**
21843      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21844      * 
21845      */
21846     stylesheets: false,
21847     
21848     // id of frame..
21849     frameId: false,
21850     
21851     // private properties
21852     validationEvent : false,
21853     deferHeight: true,
21854     initialized : false,
21855     activated : false,
21856     
21857     onFocus : Roo.emptyFn,
21858     iframePad:3,
21859     hideMode:'offsets',
21860     
21861     
21862     tbContainer : false,
21863     
21864     toolbarContainer :function() {
21865         return this.wrap.select('.x-html-editor-tb',true).first();
21866     },
21867
21868     /**
21869      * Protected method that will not generally be called directly. It
21870      * is called when the editor creates its toolbar. Override this method if you need to
21871      * add custom toolbar buttons.
21872      * @param {HtmlEditor} editor
21873      */
21874     createToolbar : function(){
21875         
21876         Roo.log("create toolbars");
21877         
21878         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21879         this.toolbars[0].render(this.toolbarContainer());
21880         
21881         return;
21882         
21883 //        if (!editor.toolbars || !editor.toolbars.length) {
21884 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21885 //        }
21886 //        
21887 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21888 //            editor.toolbars[i] = Roo.factory(
21889 //                    typeof(editor.toolbars[i]) == 'string' ?
21890 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21891 //                Roo.bootstrap.HtmlEditor);
21892 //            editor.toolbars[i].init(editor);
21893 //        }
21894     },
21895
21896      
21897     // private
21898     onRender : function(ct, position)
21899     {
21900        // Roo.log("Call onRender: " + this.xtype);
21901         var _t = this;
21902         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21903       
21904         this.wrap = this.inputEl().wrap({
21905             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21906         });
21907         
21908         this.editorcore.onRender(ct, position);
21909          
21910         if (this.resizable) {
21911             this.resizeEl = new Roo.Resizable(this.wrap, {
21912                 pinned : true,
21913                 wrap: true,
21914                 dynamic : true,
21915                 minHeight : this.height,
21916                 height: this.height,
21917                 handles : this.resizable,
21918                 width: this.width,
21919                 listeners : {
21920                     resize : function(r, w, h) {
21921                         _t.onResize(w,h); // -something
21922                     }
21923                 }
21924             });
21925             
21926         }
21927         this.createToolbar(this);
21928        
21929         
21930         if(!this.width && this.resizable){
21931             this.setSize(this.wrap.getSize());
21932         }
21933         if (this.resizeEl) {
21934             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21935             // should trigger onReize..
21936         }
21937         
21938     },
21939
21940     // private
21941     onResize : function(w, h)
21942     {
21943         Roo.log('resize: ' +w + ',' + h );
21944         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21945         var ew = false;
21946         var eh = false;
21947         
21948         if(this.inputEl() ){
21949             if(typeof w == 'number'){
21950                 var aw = w - this.wrap.getFrameWidth('lr');
21951                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21952                 ew = aw;
21953             }
21954             if(typeof h == 'number'){
21955                  var tbh = -11;  // fixme it needs to tool bar size!
21956                 for (var i =0; i < this.toolbars.length;i++) {
21957                     // fixme - ask toolbars for heights?
21958                     tbh += this.toolbars[i].el.getHeight();
21959                     //if (this.toolbars[i].footer) {
21960                     //    tbh += this.toolbars[i].footer.el.getHeight();
21961                     //}
21962                 }
21963               
21964                 
21965                 
21966                 
21967                 
21968                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21969                 ah -= 5; // knock a few pixes off for look..
21970                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21971                 var eh = ah;
21972             }
21973         }
21974         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21975         this.editorcore.onResize(ew,eh);
21976         
21977     },
21978
21979     /**
21980      * Toggles the editor between standard and source edit mode.
21981      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21982      */
21983     toggleSourceEdit : function(sourceEditMode)
21984     {
21985         this.editorcore.toggleSourceEdit(sourceEditMode);
21986         
21987         if(this.editorcore.sourceEditMode){
21988             Roo.log('editor - showing textarea');
21989             
21990 //            Roo.log('in');
21991 //            Roo.log(this.syncValue());
21992             this.syncValue();
21993             this.inputEl().removeClass(['hide', 'x-hidden']);
21994             this.inputEl().dom.removeAttribute('tabIndex');
21995             this.inputEl().focus();
21996         }else{
21997             Roo.log('editor - hiding textarea');
21998 //            Roo.log('out')
21999 //            Roo.log(this.pushValue()); 
22000             this.pushValue();
22001             
22002             this.inputEl().addClass(['hide', 'x-hidden']);
22003             this.inputEl().dom.setAttribute('tabIndex', -1);
22004             //this.deferFocus();
22005         }
22006          
22007         if(this.resizable){
22008             this.setSize(this.wrap.getSize());
22009         }
22010         
22011         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22012     },
22013  
22014     // private (for BoxComponent)
22015     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22016
22017     // private (for BoxComponent)
22018     getResizeEl : function(){
22019         return this.wrap;
22020     },
22021
22022     // private (for BoxComponent)
22023     getPositionEl : function(){
22024         return this.wrap;
22025     },
22026
22027     // private
22028     initEvents : function(){
22029         this.originalValue = this.getValue();
22030     },
22031
22032 //    /**
22033 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22034 //     * @method
22035 //     */
22036 //    markInvalid : Roo.emptyFn,
22037 //    /**
22038 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22039 //     * @method
22040 //     */
22041 //    clearInvalid : Roo.emptyFn,
22042
22043     setValue : function(v){
22044         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22045         this.editorcore.pushValue();
22046     },
22047
22048      
22049     // private
22050     deferFocus : function(){
22051         this.focus.defer(10, this);
22052     },
22053
22054     // doc'ed in Field
22055     focus : function(){
22056         this.editorcore.focus();
22057         
22058     },
22059       
22060
22061     // private
22062     onDestroy : function(){
22063         
22064         
22065         
22066         if(this.rendered){
22067             
22068             for (var i =0; i < this.toolbars.length;i++) {
22069                 // fixme - ask toolbars for heights?
22070                 this.toolbars[i].onDestroy();
22071             }
22072             
22073             this.wrap.dom.innerHTML = '';
22074             this.wrap.remove();
22075         }
22076     },
22077
22078     // private
22079     onFirstFocus : function(){
22080         //Roo.log("onFirstFocus");
22081         this.editorcore.onFirstFocus();
22082          for (var i =0; i < this.toolbars.length;i++) {
22083             this.toolbars[i].onFirstFocus();
22084         }
22085         
22086     },
22087     
22088     // private
22089     syncValue : function()
22090     {   
22091         this.editorcore.syncValue();
22092     },
22093     
22094     pushValue : function()
22095     {   
22096         this.editorcore.pushValue();
22097     }
22098      
22099     
22100     // hide stuff that is not compatible
22101     /**
22102      * @event blur
22103      * @hide
22104      */
22105     /**
22106      * @event change
22107      * @hide
22108      */
22109     /**
22110      * @event focus
22111      * @hide
22112      */
22113     /**
22114      * @event specialkey
22115      * @hide
22116      */
22117     /**
22118      * @cfg {String} fieldClass @hide
22119      */
22120     /**
22121      * @cfg {String} focusClass @hide
22122      */
22123     /**
22124      * @cfg {String} autoCreate @hide
22125      */
22126     /**
22127      * @cfg {String} inputType @hide
22128      */
22129     /**
22130      * @cfg {String} invalidClass @hide
22131      */
22132     /**
22133      * @cfg {String} invalidText @hide
22134      */
22135     /**
22136      * @cfg {String} msgFx @hide
22137      */
22138     /**
22139      * @cfg {String} validateOnBlur @hide
22140      */
22141 });
22142  
22143     
22144    
22145    
22146    
22147       
22148 Roo.namespace('Roo.bootstrap.htmleditor');
22149 /**
22150  * @class Roo.bootstrap.HtmlEditorToolbar1
22151  * Basic Toolbar
22152  * 
22153  * Usage:
22154  *
22155  new Roo.bootstrap.HtmlEditor({
22156     ....
22157     toolbars : [
22158         new Roo.bootstrap.HtmlEditorToolbar1({
22159             disable : { fonts: 1 , format: 1, ..., ... , ...],
22160             btns : [ .... ]
22161         })
22162     }
22163      
22164  * 
22165  * @cfg {Object} disable List of elements to disable..
22166  * @cfg {Array} btns List of additional buttons.
22167  * 
22168  * 
22169  * NEEDS Extra CSS? 
22170  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22171  */
22172  
22173 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22174 {
22175     
22176     Roo.apply(this, config);
22177     
22178     // default disabled, based on 'good practice'..
22179     this.disable = this.disable || {};
22180     Roo.applyIf(this.disable, {
22181         fontSize : true,
22182         colors : true,
22183         specialElements : true
22184     });
22185     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22186     
22187     this.editor = config.editor;
22188     this.editorcore = config.editor.editorcore;
22189     
22190     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22191     
22192     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22193     // dont call parent... till later.
22194 }
22195 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22196      
22197     bar : true,
22198     
22199     editor : false,
22200     editorcore : false,
22201     
22202     
22203     formats : [
22204         "p" ,  
22205         "h1","h2","h3","h4","h5","h6", 
22206         "pre", "code", 
22207         "abbr", "acronym", "address", "cite", "samp", "var",
22208         'div','span'
22209     ],
22210     
22211     onRender : function(ct, position)
22212     {
22213        // Roo.log("Call onRender: " + this.xtype);
22214         
22215        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22216        Roo.log(this.el);
22217        this.el.dom.style.marginBottom = '0';
22218        var _this = this;
22219        var editorcore = this.editorcore;
22220        var editor= this.editor;
22221        
22222        var children = [];
22223        var btn = function(id,cmd , toggle, handler){
22224        
22225             var  event = toggle ? 'toggle' : 'click';
22226        
22227             var a = {
22228                 size : 'sm',
22229                 xtype: 'Button',
22230                 xns: Roo.bootstrap,
22231                 glyphicon : id,
22232                 cmd : id || cmd,
22233                 enableToggle:toggle !== false,
22234                 //html : 'submit'
22235                 pressed : toggle ? false : null,
22236                 listeners : {}
22237             };
22238             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22239                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22240             };
22241             children.push(a);
22242             return a;
22243        }
22244         
22245         var style = {
22246                 xtype: 'Button',
22247                 size : 'sm',
22248                 xns: Roo.bootstrap,
22249                 glyphicon : 'font',
22250                 //html : 'submit'
22251                 menu : {
22252                     xtype: 'Menu',
22253                     xns: Roo.bootstrap,
22254                     items:  []
22255                 }
22256         };
22257         Roo.each(this.formats, function(f) {
22258             style.menu.items.push({
22259                 xtype :'MenuItem',
22260                 xns: Roo.bootstrap,
22261                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22262                 tagname : f,
22263                 listeners : {
22264                     click : function()
22265                     {
22266                         editorcore.insertTag(this.tagname);
22267                         editor.focus();
22268                     }
22269                 }
22270                 
22271             });
22272         });
22273          children.push(style);   
22274             
22275             
22276         btn('bold',false,true);
22277         btn('italic',false,true);
22278         btn('align-left', 'justifyleft',true);
22279         btn('align-center', 'justifycenter',true);
22280         btn('align-right' , 'justifyright',true);
22281         btn('link', false, false, function(btn) {
22282             //Roo.log("create link?");
22283             var url = prompt(this.createLinkText, this.defaultLinkValue);
22284             if(url && url != 'http:/'+'/'){
22285                 this.editorcore.relayCmd('createlink', url);
22286             }
22287         }),
22288         btn('list','insertunorderedlist',true);
22289         btn('pencil', false,true, function(btn){
22290                 Roo.log(this);
22291                 
22292                 this.toggleSourceEdit(btn.pressed);
22293         });
22294         /*
22295         var cog = {
22296                 xtype: 'Button',
22297                 size : 'sm',
22298                 xns: Roo.bootstrap,
22299                 glyphicon : 'cog',
22300                 //html : 'submit'
22301                 menu : {
22302                     xtype: 'Menu',
22303                     xns: Roo.bootstrap,
22304                     items:  []
22305                 }
22306         };
22307         
22308         cog.menu.items.push({
22309             xtype :'MenuItem',
22310             xns: Roo.bootstrap,
22311             html : Clean styles,
22312             tagname : f,
22313             listeners : {
22314                 click : function()
22315                 {
22316                     editorcore.insertTag(this.tagname);
22317                     editor.focus();
22318                 }
22319             }
22320             
22321         });
22322        */
22323         
22324          
22325        this.xtype = 'NavSimplebar';
22326         
22327         for(var i=0;i< children.length;i++) {
22328             
22329             this.buttons.add(this.addxtypeChild(children[i]));
22330             
22331         }
22332         
22333         editor.on('editorevent', this.updateToolbar, this);
22334     },
22335     onBtnClick : function(id)
22336     {
22337        this.editorcore.relayCmd(id);
22338        this.editorcore.focus();
22339     },
22340     
22341     /**
22342      * Protected method that will not generally be called directly. It triggers
22343      * a toolbar update by reading the markup state of the current selection in the editor.
22344      */
22345     updateToolbar: function(){
22346
22347         if(!this.editorcore.activated){
22348             this.editor.onFirstFocus(); // is this neeed?
22349             return;
22350         }
22351
22352         var btns = this.buttons; 
22353         var doc = this.editorcore.doc;
22354         btns.get('bold').setActive(doc.queryCommandState('bold'));
22355         btns.get('italic').setActive(doc.queryCommandState('italic'));
22356         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22357         
22358         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22359         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22360         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22361         
22362         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22363         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22364          /*
22365         
22366         var ans = this.editorcore.getAllAncestors();
22367         if (this.formatCombo) {
22368             
22369             
22370             var store = this.formatCombo.store;
22371             this.formatCombo.setValue("");
22372             for (var i =0; i < ans.length;i++) {
22373                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22374                     // select it..
22375                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22376                     break;
22377                 }
22378             }
22379         }
22380         
22381         
22382         
22383         // hides menus... - so this cant be on a menu...
22384         Roo.bootstrap.MenuMgr.hideAll();
22385         */
22386         Roo.bootstrap.MenuMgr.hideAll();
22387         //this.editorsyncValue();
22388     },
22389     onFirstFocus: function() {
22390         this.buttons.each(function(item){
22391            item.enable();
22392         });
22393     },
22394     toggleSourceEdit : function(sourceEditMode){
22395         
22396           
22397         if(sourceEditMode){
22398             Roo.log("disabling buttons");
22399            this.buttons.each( function(item){
22400                 if(item.cmd != 'pencil'){
22401                     item.disable();
22402                 }
22403             });
22404           
22405         }else{
22406             Roo.log("enabling buttons");
22407             if(this.editorcore.initialized){
22408                 this.buttons.each( function(item){
22409                     item.enable();
22410                 });
22411             }
22412             
22413         }
22414         Roo.log("calling toggole on editor");
22415         // tell the editor that it's been pressed..
22416         this.editor.toggleSourceEdit(sourceEditMode);
22417        
22418     }
22419 });
22420
22421
22422
22423
22424
22425 /**
22426  * @class Roo.bootstrap.Table.AbstractSelectionModel
22427  * @extends Roo.util.Observable
22428  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22429  * implemented by descendant classes.  This class should not be directly instantiated.
22430  * @constructor
22431  */
22432 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22433     this.locked = false;
22434     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22435 };
22436
22437
22438 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22439     /** @ignore Called by the grid automatically. Do not call directly. */
22440     init : function(grid){
22441         this.grid = grid;
22442         this.initEvents();
22443     },
22444
22445     /**
22446      * Locks the selections.
22447      */
22448     lock : function(){
22449         this.locked = true;
22450     },
22451
22452     /**
22453      * Unlocks the selections.
22454      */
22455     unlock : function(){
22456         this.locked = false;
22457     },
22458
22459     /**
22460      * Returns true if the selections are locked.
22461      * @return {Boolean}
22462      */
22463     isLocked : function(){
22464         return this.locked;
22465     }
22466 });
22467 /**
22468  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22469  * @class Roo.bootstrap.Table.RowSelectionModel
22470  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22471  * It supports multiple selections and keyboard selection/navigation. 
22472  * @constructor
22473  * @param {Object} config
22474  */
22475
22476 Roo.bootstrap.Table.RowSelectionModel = function(config){
22477     Roo.apply(this, config);
22478     this.selections = new Roo.util.MixedCollection(false, function(o){
22479         return o.id;
22480     });
22481
22482     this.last = false;
22483     this.lastActive = false;
22484
22485     this.addEvents({
22486         /**
22487              * @event selectionchange
22488              * Fires when the selection changes
22489              * @param {SelectionModel} this
22490              */
22491             "selectionchange" : true,
22492         /**
22493              * @event afterselectionchange
22494              * Fires after the selection changes (eg. by key press or clicking)
22495              * @param {SelectionModel} this
22496              */
22497             "afterselectionchange" : true,
22498         /**
22499              * @event beforerowselect
22500              * Fires when a row is selected being selected, return false to cancel.
22501              * @param {SelectionModel} this
22502              * @param {Number} rowIndex The selected index
22503              * @param {Boolean} keepExisting False if other selections will be cleared
22504              */
22505             "beforerowselect" : true,
22506         /**
22507              * @event rowselect
22508              * Fires when a row is selected.
22509              * @param {SelectionModel} this
22510              * @param {Number} rowIndex The selected index
22511              * @param {Roo.data.Record} r The record
22512              */
22513             "rowselect" : true,
22514         /**
22515              * @event rowdeselect
22516              * Fires when a row is deselected.
22517              * @param {SelectionModel} this
22518              * @param {Number} rowIndex The selected index
22519              */
22520         "rowdeselect" : true
22521     });
22522     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22523     this.locked = false;
22524  };
22525
22526 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22527     /**
22528      * @cfg {Boolean} singleSelect
22529      * True to allow selection of only one row at a time (defaults to false)
22530      */
22531     singleSelect : false,
22532
22533     // private
22534     initEvents : function()
22535     {
22536
22537         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22538         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22539         //}else{ // allow click to work like normal
22540          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22541         //}
22542         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22543         this.grid.on("rowclick", this.handleMouseDown, this);
22544         
22545         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22546             "up" : function(e){
22547                 if(!e.shiftKey){
22548                     this.selectPrevious(e.shiftKey);
22549                 }else if(this.last !== false && this.lastActive !== false){
22550                     var last = this.last;
22551                     this.selectRange(this.last,  this.lastActive-1);
22552                     this.grid.getView().focusRow(this.lastActive);
22553                     if(last !== false){
22554                         this.last = last;
22555                     }
22556                 }else{
22557                     this.selectFirstRow();
22558                 }
22559                 this.fireEvent("afterselectionchange", this);
22560             },
22561             "down" : function(e){
22562                 if(!e.shiftKey){
22563                     this.selectNext(e.shiftKey);
22564                 }else if(this.last !== false && this.lastActive !== false){
22565                     var last = this.last;
22566                     this.selectRange(this.last,  this.lastActive+1);
22567                     this.grid.getView().focusRow(this.lastActive);
22568                     if(last !== false){
22569                         this.last = last;
22570                     }
22571                 }else{
22572                     this.selectFirstRow();
22573                 }
22574                 this.fireEvent("afterselectionchange", this);
22575             },
22576             scope: this
22577         });
22578         this.grid.store.on('load', function(){
22579             this.selections.clear();
22580         },this);
22581         /*
22582         var view = this.grid.view;
22583         view.on("refresh", this.onRefresh, this);
22584         view.on("rowupdated", this.onRowUpdated, this);
22585         view.on("rowremoved", this.onRemove, this);
22586         */
22587     },
22588
22589     // private
22590     onRefresh : function()
22591     {
22592         var ds = this.grid.store, i, v = this.grid.view;
22593         var s = this.selections;
22594         s.each(function(r){
22595             if((i = ds.indexOfId(r.id)) != -1){
22596                 v.onRowSelect(i);
22597             }else{
22598                 s.remove(r);
22599             }
22600         });
22601     },
22602
22603     // private
22604     onRemove : function(v, index, r){
22605         this.selections.remove(r);
22606     },
22607
22608     // private
22609     onRowUpdated : function(v, index, r){
22610         if(this.isSelected(r)){
22611             v.onRowSelect(index);
22612         }
22613     },
22614
22615     /**
22616      * Select records.
22617      * @param {Array} records The records to select
22618      * @param {Boolean} keepExisting (optional) True to keep existing selections
22619      */
22620     selectRecords : function(records, keepExisting)
22621     {
22622         if(!keepExisting){
22623             this.clearSelections();
22624         }
22625             var ds = this.grid.store;
22626         for(var i = 0, len = records.length; i < len; i++){
22627             this.selectRow(ds.indexOf(records[i]), true);
22628         }
22629     },
22630
22631     /**
22632      * Gets the number of selected rows.
22633      * @return {Number}
22634      */
22635     getCount : function(){
22636         return this.selections.length;
22637     },
22638
22639     /**
22640      * Selects the first row in the grid.
22641      */
22642     selectFirstRow : function(){
22643         this.selectRow(0);
22644     },
22645
22646     /**
22647      * Select the last row.
22648      * @param {Boolean} keepExisting (optional) True to keep existing selections
22649      */
22650     selectLastRow : function(keepExisting){
22651         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22652         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22653     },
22654
22655     /**
22656      * Selects the row immediately following the last selected row.
22657      * @param {Boolean} keepExisting (optional) True to keep existing selections
22658      */
22659     selectNext : function(keepExisting)
22660     {
22661             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22662             this.selectRow(this.last+1, keepExisting);
22663             this.grid.getView().focusRow(this.last);
22664         }
22665     },
22666
22667     /**
22668      * Selects the row that precedes the last selected row.
22669      * @param {Boolean} keepExisting (optional) True to keep existing selections
22670      */
22671     selectPrevious : function(keepExisting){
22672         if(this.last){
22673             this.selectRow(this.last-1, keepExisting);
22674             this.grid.getView().focusRow(this.last);
22675         }
22676     },
22677
22678     /**
22679      * Returns the selected records
22680      * @return {Array} Array of selected records
22681      */
22682     getSelections : function(){
22683         return [].concat(this.selections.items);
22684     },
22685
22686     /**
22687      * Returns the first selected record.
22688      * @return {Record}
22689      */
22690     getSelected : function(){
22691         return this.selections.itemAt(0);
22692     },
22693
22694
22695     /**
22696      * Clears all selections.
22697      */
22698     clearSelections : function(fast)
22699     {
22700         if(this.locked) {
22701             return;
22702         }
22703         if(fast !== true){
22704                 var ds = this.grid.store;
22705             var s = this.selections;
22706             s.each(function(r){
22707                 this.deselectRow(ds.indexOfId(r.id));
22708             }, this);
22709             s.clear();
22710         }else{
22711             this.selections.clear();
22712         }
22713         this.last = false;
22714     },
22715
22716
22717     /**
22718      * Selects all rows.
22719      */
22720     selectAll : function(){
22721         if(this.locked) {
22722             return;
22723         }
22724         this.selections.clear();
22725         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22726             this.selectRow(i, true);
22727         }
22728     },
22729
22730     /**
22731      * Returns True if there is a selection.
22732      * @return {Boolean}
22733      */
22734     hasSelection : function(){
22735         return this.selections.length > 0;
22736     },
22737
22738     /**
22739      * Returns True if the specified row is selected.
22740      * @param {Number/Record} record The record or index of the record to check
22741      * @return {Boolean}
22742      */
22743     isSelected : function(index){
22744             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22745         return (r && this.selections.key(r.id) ? true : false);
22746     },
22747
22748     /**
22749      * Returns True if the specified record id is selected.
22750      * @param {String} id The id of record to check
22751      * @return {Boolean}
22752      */
22753     isIdSelected : function(id){
22754         return (this.selections.key(id) ? true : false);
22755     },
22756
22757
22758     // private
22759     handleMouseDBClick : function(e, t){
22760         
22761     },
22762     // private
22763     handleMouseDown : function(e, t)
22764     {
22765             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22766         if(this.isLocked() || rowIndex < 0 ){
22767             return;
22768         };
22769         if(e.shiftKey && this.last !== false){
22770             var last = this.last;
22771             this.selectRange(last, rowIndex, e.ctrlKey);
22772             this.last = last; // reset the last
22773             t.focus();
22774     
22775         }else{
22776             var isSelected = this.isSelected(rowIndex);
22777             //Roo.log("select row:" + rowIndex);
22778             if(isSelected){
22779                 this.deselectRow(rowIndex);
22780             } else {
22781                         this.selectRow(rowIndex, true);
22782             }
22783     
22784             /*
22785                 if(e.button !== 0 && isSelected){
22786                 alert('rowIndex 2: ' + rowIndex);
22787                     view.focusRow(rowIndex);
22788                 }else if(e.ctrlKey && isSelected){
22789                     this.deselectRow(rowIndex);
22790                 }else if(!isSelected){
22791                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22792                     view.focusRow(rowIndex);
22793                 }
22794             */
22795         }
22796         this.fireEvent("afterselectionchange", this);
22797     },
22798     // private
22799     handleDragableRowClick :  function(grid, rowIndex, e) 
22800     {
22801         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22802             this.selectRow(rowIndex, false);
22803             grid.view.focusRow(rowIndex);
22804              this.fireEvent("afterselectionchange", this);
22805         }
22806     },
22807     
22808     /**
22809      * Selects multiple rows.
22810      * @param {Array} rows Array of the indexes of the row to select
22811      * @param {Boolean} keepExisting (optional) True to keep existing selections
22812      */
22813     selectRows : function(rows, keepExisting){
22814         if(!keepExisting){
22815             this.clearSelections();
22816         }
22817         for(var i = 0, len = rows.length; i < len; i++){
22818             this.selectRow(rows[i], true);
22819         }
22820     },
22821
22822     /**
22823      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22824      * @param {Number} startRow The index of the first row in the range
22825      * @param {Number} endRow The index of the last row in the range
22826      * @param {Boolean} keepExisting (optional) True to retain existing selections
22827      */
22828     selectRange : function(startRow, endRow, keepExisting){
22829         if(this.locked) {
22830             return;
22831         }
22832         if(!keepExisting){
22833             this.clearSelections();
22834         }
22835         if(startRow <= endRow){
22836             for(var i = startRow; i <= endRow; i++){
22837                 this.selectRow(i, true);
22838             }
22839         }else{
22840             for(var i = startRow; i >= endRow; i--){
22841                 this.selectRow(i, true);
22842             }
22843         }
22844     },
22845
22846     /**
22847      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22848      * @param {Number} startRow The index of the first row in the range
22849      * @param {Number} endRow The index of the last row in the range
22850      */
22851     deselectRange : function(startRow, endRow, preventViewNotify){
22852         if(this.locked) {
22853             return;
22854         }
22855         for(var i = startRow; i <= endRow; i++){
22856             this.deselectRow(i, preventViewNotify);
22857         }
22858     },
22859
22860     /**
22861      * Selects a row.
22862      * @param {Number} row The index of the row to select
22863      * @param {Boolean} keepExisting (optional) True to keep existing selections
22864      */
22865     selectRow : function(index, keepExisting, preventViewNotify)
22866     {
22867             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22868             return;
22869         }
22870         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22871             if(!keepExisting || this.singleSelect){
22872                 this.clearSelections();
22873             }
22874             
22875             var r = this.grid.store.getAt(index);
22876             //console.log('selectRow - record id :' + r.id);
22877             
22878             this.selections.add(r);
22879             this.last = this.lastActive = index;
22880             if(!preventViewNotify){
22881                 var proxy = new Roo.Element(
22882                                 this.grid.getRowDom(index)
22883                 );
22884                 proxy.addClass('bg-info info');
22885             }
22886             this.fireEvent("rowselect", this, index, r);
22887             this.fireEvent("selectionchange", this);
22888         }
22889     },
22890
22891     /**
22892      * Deselects a row.
22893      * @param {Number} row The index of the row to deselect
22894      */
22895     deselectRow : function(index, preventViewNotify)
22896     {
22897         if(this.locked) {
22898             return;
22899         }
22900         if(this.last == index){
22901             this.last = false;
22902         }
22903         if(this.lastActive == index){
22904             this.lastActive = false;
22905         }
22906         
22907         var r = this.grid.store.getAt(index);
22908         if (!r) {
22909             return;
22910         }
22911         
22912         this.selections.remove(r);
22913         //.console.log('deselectRow - record id :' + r.id);
22914         if(!preventViewNotify){
22915         
22916             var proxy = new Roo.Element(
22917                 this.grid.getRowDom(index)
22918             );
22919             proxy.removeClass('bg-info info');
22920         }
22921         this.fireEvent("rowdeselect", this, index);
22922         this.fireEvent("selectionchange", this);
22923     },
22924
22925     // private
22926     restoreLast : function(){
22927         if(this._last){
22928             this.last = this._last;
22929         }
22930     },
22931
22932     // private
22933     acceptsNav : function(row, col, cm){
22934         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22935     },
22936
22937     // private
22938     onEditorKey : function(field, e){
22939         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22940         if(k == e.TAB){
22941             e.stopEvent();
22942             ed.completeEdit();
22943             if(e.shiftKey){
22944                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22945             }else{
22946                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22947             }
22948         }else if(k == e.ENTER && !e.ctrlKey){
22949             e.stopEvent();
22950             ed.completeEdit();
22951             if(e.shiftKey){
22952                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22953             }else{
22954                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22955             }
22956         }else if(k == e.ESC){
22957             ed.cancelEdit();
22958         }
22959         if(newCell){
22960             g.startEditing(newCell[0], newCell[1]);
22961         }
22962     }
22963 });
22964 /*
22965  * Based on:
22966  * Ext JS Library 1.1.1
22967  * Copyright(c) 2006-2007, Ext JS, LLC.
22968  *
22969  * Originally Released Under LGPL - original licence link has changed is not relivant.
22970  *
22971  * Fork - LGPL
22972  * <script type="text/javascript">
22973  */
22974  
22975 /**
22976  * @class Roo.bootstrap.PagingToolbar
22977  * @extends Roo.bootstrap.NavSimplebar
22978  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22979  * @constructor
22980  * Create a new PagingToolbar
22981  * @param {Object} config The config object
22982  * @param {Roo.data.Store} store
22983  */
22984 Roo.bootstrap.PagingToolbar = function(config)
22985 {
22986     // old args format still supported... - xtype is prefered..
22987         // created from xtype...
22988     
22989     this.ds = config.dataSource;
22990     
22991     if (config.store && !this.ds) {
22992         this.store= Roo.factory(config.store, Roo.data);
22993         this.ds = this.store;
22994         this.ds.xmodule = this.xmodule || false;
22995     }
22996     
22997     this.toolbarItems = [];
22998     if (config.items) {
22999         this.toolbarItems = config.items;
23000     }
23001     
23002     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23003     
23004     this.cursor = 0;
23005     
23006     if (this.ds) { 
23007         this.bind(this.ds);
23008     }
23009     
23010     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23011     
23012 };
23013
23014 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23015     /**
23016      * @cfg {Roo.data.Store} dataSource
23017      * The underlying data store providing the paged data
23018      */
23019     /**
23020      * @cfg {String/HTMLElement/Element} container
23021      * container The id or element that will contain the toolbar
23022      */
23023     /**
23024      * @cfg {Boolean} displayInfo
23025      * True to display the displayMsg (defaults to false)
23026      */
23027     /**
23028      * @cfg {Number} pageSize
23029      * The number of records to display per page (defaults to 20)
23030      */
23031     pageSize: 20,
23032     /**
23033      * @cfg {String} displayMsg
23034      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23035      */
23036     displayMsg : 'Displaying {0} - {1} of {2}',
23037     /**
23038      * @cfg {String} emptyMsg
23039      * The message to display when no records are found (defaults to "No data to display")
23040      */
23041     emptyMsg : 'No data to display',
23042     /**
23043      * Customizable piece of the default paging text (defaults to "Page")
23044      * @type String
23045      */
23046     beforePageText : "Page",
23047     /**
23048      * Customizable piece of the default paging text (defaults to "of %0")
23049      * @type String
23050      */
23051     afterPageText : "of {0}",
23052     /**
23053      * Customizable piece of the default paging text (defaults to "First Page")
23054      * @type String
23055      */
23056     firstText : "First Page",
23057     /**
23058      * Customizable piece of the default paging text (defaults to "Previous Page")
23059      * @type String
23060      */
23061     prevText : "Previous Page",
23062     /**
23063      * Customizable piece of the default paging text (defaults to "Next Page")
23064      * @type String
23065      */
23066     nextText : "Next Page",
23067     /**
23068      * Customizable piece of the default paging text (defaults to "Last Page")
23069      * @type String
23070      */
23071     lastText : "Last Page",
23072     /**
23073      * Customizable piece of the default paging text (defaults to "Refresh")
23074      * @type String
23075      */
23076     refreshText : "Refresh",
23077
23078     buttons : false,
23079     // private
23080     onRender : function(ct, position) 
23081     {
23082         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23083         this.navgroup.parentId = this.id;
23084         this.navgroup.onRender(this.el, null);
23085         // add the buttons to the navgroup
23086         
23087         if(this.displayInfo){
23088             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23089             this.displayEl = this.el.select('.x-paging-info', true).first();
23090 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23091 //            this.displayEl = navel.el.select('span',true).first();
23092         }
23093         
23094         var _this = this;
23095         
23096         if(this.buttons){
23097             Roo.each(_this.buttons, function(e){ // this might need to use render????
23098                Roo.factory(e).onRender(_this.el, null);
23099             });
23100         }
23101             
23102         Roo.each(_this.toolbarItems, function(e) {
23103             _this.navgroup.addItem(e);
23104         });
23105         
23106         
23107         this.first = this.navgroup.addItem({
23108             tooltip: this.firstText,
23109             cls: "prev",
23110             icon : 'fa fa-backward',
23111             disabled: true,
23112             preventDefault: true,
23113             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23114         });
23115         
23116         this.prev =  this.navgroup.addItem({
23117             tooltip: this.prevText,
23118             cls: "prev",
23119             icon : 'fa fa-step-backward',
23120             disabled: true,
23121             preventDefault: true,
23122             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23123         });
23124     //this.addSeparator();
23125         
23126         
23127         var field = this.navgroup.addItem( {
23128             tagtype : 'span',
23129             cls : 'x-paging-position',
23130             
23131             html : this.beforePageText  +
23132                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23133                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23134          } ); //?? escaped?
23135         
23136         this.field = field.el.select('input', true).first();
23137         this.field.on("keydown", this.onPagingKeydown, this);
23138         this.field.on("focus", function(){this.dom.select();});
23139     
23140     
23141         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23142         //this.field.setHeight(18);
23143         //this.addSeparator();
23144         this.next = this.navgroup.addItem({
23145             tooltip: this.nextText,
23146             cls: "next",
23147             html : ' <i class="fa fa-step-forward">',
23148             disabled: true,
23149             preventDefault: true,
23150             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23151         });
23152         this.last = this.navgroup.addItem({
23153             tooltip: this.lastText,
23154             icon : 'fa fa-forward',
23155             cls: "next",
23156             disabled: true,
23157             preventDefault: true,
23158             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23159         });
23160     //this.addSeparator();
23161         this.loading = this.navgroup.addItem({
23162             tooltip: this.refreshText,
23163             icon: 'fa fa-refresh',
23164             preventDefault: true,
23165             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23166         });
23167         
23168     },
23169
23170     // private
23171     updateInfo : function(){
23172         if(this.displayEl){
23173             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23174             var msg = count == 0 ?
23175                 this.emptyMsg :
23176                 String.format(
23177                     this.displayMsg,
23178                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23179                 );
23180             this.displayEl.update(msg);
23181         }
23182     },
23183
23184     // private
23185     onLoad : function(ds, r, o){
23186        this.cursor = o.params ? o.params.start : 0;
23187        var d = this.getPageData(),
23188             ap = d.activePage,
23189             ps = d.pages;
23190         
23191        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23192        this.field.dom.value = ap;
23193        this.first.setDisabled(ap == 1);
23194        this.prev.setDisabled(ap == 1);
23195        this.next.setDisabled(ap == ps);
23196        this.last.setDisabled(ap == ps);
23197        this.loading.enable();
23198        this.updateInfo();
23199     },
23200
23201     // private
23202     getPageData : function(){
23203         var total = this.ds.getTotalCount();
23204         return {
23205             total : total,
23206             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23207             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23208         };
23209     },
23210
23211     // private
23212     onLoadError : function(){
23213         this.loading.enable();
23214     },
23215
23216     // private
23217     onPagingKeydown : function(e){
23218         var k = e.getKey();
23219         var d = this.getPageData();
23220         if(k == e.RETURN){
23221             var v = this.field.dom.value, pageNum;
23222             if(!v || isNaN(pageNum = parseInt(v, 10))){
23223                 this.field.dom.value = d.activePage;
23224                 return;
23225             }
23226             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23227             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23228             e.stopEvent();
23229         }
23230         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))
23231         {
23232           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23233           this.field.dom.value = pageNum;
23234           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23235           e.stopEvent();
23236         }
23237         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23238         {
23239           var v = this.field.dom.value, pageNum; 
23240           var increment = (e.shiftKey) ? 10 : 1;
23241           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23242                 increment *= -1;
23243           }
23244           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23245             this.field.dom.value = d.activePage;
23246             return;
23247           }
23248           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23249           {
23250             this.field.dom.value = parseInt(v, 10) + increment;
23251             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23252             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23253           }
23254           e.stopEvent();
23255         }
23256     },
23257
23258     // private
23259     beforeLoad : function(){
23260         if(this.loading){
23261             this.loading.disable();
23262         }
23263     },
23264
23265     // private
23266     onClick : function(which){
23267         
23268         var ds = this.ds;
23269         if (!ds) {
23270             return;
23271         }
23272         
23273         switch(which){
23274             case "first":
23275                 ds.load({params:{start: 0, limit: this.pageSize}});
23276             break;
23277             case "prev":
23278                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23279             break;
23280             case "next":
23281                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23282             break;
23283             case "last":
23284                 var total = ds.getTotalCount();
23285                 var extra = total % this.pageSize;
23286                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23287                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23288             break;
23289             case "refresh":
23290                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23291             break;
23292         }
23293     },
23294
23295     /**
23296      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23297      * @param {Roo.data.Store} store The data store to unbind
23298      */
23299     unbind : function(ds){
23300         ds.un("beforeload", this.beforeLoad, this);
23301         ds.un("load", this.onLoad, this);
23302         ds.un("loadexception", this.onLoadError, this);
23303         ds.un("remove", this.updateInfo, this);
23304         ds.un("add", this.updateInfo, this);
23305         this.ds = undefined;
23306     },
23307
23308     /**
23309      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23310      * @param {Roo.data.Store} store The data store to bind
23311      */
23312     bind : function(ds){
23313         ds.on("beforeload", this.beforeLoad, this);
23314         ds.on("load", this.onLoad, this);
23315         ds.on("loadexception", this.onLoadError, this);
23316         ds.on("remove", this.updateInfo, this);
23317         ds.on("add", this.updateInfo, this);
23318         this.ds = ds;
23319     }
23320 });/*
23321  * - LGPL
23322  *
23323  * element
23324  * 
23325  */
23326
23327 /**
23328  * @class Roo.bootstrap.MessageBar
23329  * @extends Roo.bootstrap.Component
23330  * Bootstrap MessageBar class
23331  * @cfg {String} html contents of the MessageBar
23332  * @cfg {String} weight (info | success | warning | danger) default info
23333  * @cfg {String} beforeClass insert the bar before the given class
23334  * @cfg {Boolean} closable (true | false) default false
23335  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23336  * 
23337  * @constructor
23338  * Create a new Element
23339  * @param {Object} config The config object
23340  */
23341
23342 Roo.bootstrap.MessageBar = function(config){
23343     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23344 };
23345
23346 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23347     
23348     html: '',
23349     weight: 'info',
23350     closable: false,
23351     fixed: false,
23352     beforeClass: 'bootstrap-sticky-wrap',
23353     
23354     getAutoCreate : function(){
23355         
23356         var cfg = {
23357             tag: 'div',
23358             cls: 'alert alert-dismissable alert-' + this.weight,
23359             cn: [
23360                 {
23361                     tag: 'span',
23362                     cls: 'message',
23363                     html: this.html || ''
23364                 }
23365             ]
23366         };
23367         
23368         if(this.fixed){
23369             cfg.cls += ' alert-messages-fixed';
23370         }
23371         
23372         if(this.closable){
23373             cfg.cn.push({
23374                 tag: 'button',
23375                 cls: 'close',
23376                 html: 'x'
23377             });
23378         }
23379         
23380         return cfg;
23381     },
23382     
23383     onRender : function(ct, position)
23384     {
23385         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23386         
23387         if(!this.el){
23388             var cfg = Roo.apply({},  this.getAutoCreate());
23389             cfg.id = Roo.id();
23390             
23391             if (this.cls) {
23392                 cfg.cls += ' ' + this.cls;
23393             }
23394             if (this.style) {
23395                 cfg.style = this.style;
23396             }
23397             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23398             
23399             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23400         }
23401         
23402         this.el.select('>button.close').on('click', this.hide, this);
23403         
23404     },
23405     
23406     show : function()
23407     {
23408         if (!this.rendered) {
23409             this.render();
23410         }
23411         
23412         this.el.show();
23413         
23414         this.fireEvent('show', this);
23415         
23416     },
23417     
23418     hide : function()
23419     {
23420         if (!this.rendered) {
23421             this.render();
23422         }
23423         
23424         this.el.hide();
23425         
23426         this.fireEvent('hide', this);
23427     },
23428     
23429     update : function()
23430     {
23431 //        var e = this.el.dom.firstChild;
23432 //        
23433 //        if(this.closable){
23434 //            e = e.nextSibling;
23435 //        }
23436 //        
23437 //        e.data = this.html || '';
23438
23439         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23440     }
23441    
23442 });
23443
23444  
23445
23446      /*
23447  * - LGPL
23448  *
23449  * Graph
23450  * 
23451  */
23452
23453
23454 /**
23455  * @class Roo.bootstrap.Graph
23456  * @extends Roo.bootstrap.Component
23457  * Bootstrap Graph class
23458 > Prameters
23459  -sm {number} sm 4
23460  -md {number} md 5
23461  @cfg {String} graphtype  bar | vbar | pie
23462  @cfg {number} g_x coodinator | centre x (pie)
23463  @cfg {number} g_y coodinator | centre y (pie)
23464  @cfg {number} g_r radius (pie)
23465  @cfg {number} g_height height of the chart (respected by all elements in the set)
23466  @cfg {number} g_width width of the chart (respected by all elements in the set)
23467  @cfg {Object} title The title of the chart
23468     
23469  -{Array}  values
23470  -opts (object) options for the chart 
23471      o {
23472      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23473      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23474      o vgutter (number)
23475      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.
23476      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23477      o to
23478      o stretch (boolean)
23479      o }
23480  -opts (object) options for the pie
23481      o{
23482      o cut
23483      o startAngle (number)
23484      o endAngle (number)
23485      } 
23486  *
23487  * @constructor
23488  * Create a new Input
23489  * @param {Object} config The config object
23490  */
23491
23492 Roo.bootstrap.Graph = function(config){
23493     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23494     
23495     this.addEvents({
23496         // img events
23497         /**
23498          * @event click
23499          * The img click event for the img.
23500          * @param {Roo.EventObject} e
23501          */
23502         "click" : true
23503     });
23504 };
23505
23506 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23507     
23508     sm: 4,
23509     md: 5,
23510     graphtype: 'bar',
23511     g_height: 250,
23512     g_width: 400,
23513     g_x: 50,
23514     g_y: 50,
23515     g_r: 30,
23516     opts:{
23517         //g_colors: this.colors,
23518         g_type: 'soft',
23519         g_gutter: '20%'
23520
23521     },
23522     title : false,
23523
23524     getAutoCreate : function(){
23525         
23526         var cfg = {
23527             tag: 'div',
23528             html : null
23529         };
23530         
23531         
23532         return  cfg;
23533     },
23534
23535     onRender : function(ct,position){
23536         
23537         
23538         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23539         
23540         if (typeof(Raphael) == 'undefined') {
23541             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23542             return;
23543         }
23544         
23545         this.raphael = Raphael(this.el.dom);
23546         
23547                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23548                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23549                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23550                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23551                 /*
23552                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23553                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23554                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23555                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23556                 
23557                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23558                 r.barchart(330, 10, 300, 220, data1);
23559                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23560                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23561                 */
23562                 
23563                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23564                 // r.barchart(30, 30, 560, 250,  xdata, {
23565                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23566                 //     axis : "0 0 1 1",
23567                 //     axisxlabels :  xdata
23568                 //     //yvalues : cols,
23569                    
23570                 // });
23571 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23572 //        
23573 //        this.load(null,xdata,{
23574 //                axis : "0 0 1 1",
23575 //                axisxlabels :  xdata
23576 //                });
23577
23578     },
23579
23580     load : function(graphtype,xdata,opts)
23581     {
23582         this.raphael.clear();
23583         if(!graphtype) {
23584             graphtype = this.graphtype;
23585         }
23586         if(!opts){
23587             opts = this.opts;
23588         }
23589         var r = this.raphael,
23590             fin = function () {
23591                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23592             },
23593             fout = function () {
23594                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23595             },
23596             pfin = function() {
23597                 this.sector.stop();
23598                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23599
23600                 if (this.label) {
23601                     this.label[0].stop();
23602                     this.label[0].attr({ r: 7.5 });
23603                     this.label[1].attr({ "font-weight": 800 });
23604                 }
23605             },
23606             pfout = function() {
23607                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23608
23609                 if (this.label) {
23610                     this.label[0].animate({ r: 5 }, 500, "bounce");
23611                     this.label[1].attr({ "font-weight": 400 });
23612                 }
23613             };
23614
23615         switch(graphtype){
23616             case 'bar':
23617                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23618                 break;
23619             case 'hbar':
23620                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23621                 break;
23622             case 'pie':
23623 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23624 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23625 //            
23626                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23627                 
23628                 break;
23629
23630         }
23631         
23632         if(this.title){
23633             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23634         }
23635         
23636     },
23637     
23638     setTitle: function(o)
23639     {
23640         this.title = o;
23641     },
23642     
23643     initEvents: function() {
23644         
23645         if(!this.href){
23646             this.el.on('click', this.onClick, this);
23647         }
23648     },
23649     
23650     onClick : function(e)
23651     {
23652         Roo.log('img onclick');
23653         this.fireEvent('click', this, e);
23654     }
23655    
23656 });
23657
23658  
23659 /*
23660  * - LGPL
23661  *
23662  * numberBox
23663  * 
23664  */
23665 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23666
23667 /**
23668  * @class Roo.bootstrap.dash.NumberBox
23669  * @extends Roo.bootstrap.Component
23670  * Bootstrap NumberBox class
23671  * @cfg {String} headline Box headline
23672  * @cfg {String} content Box content
23673  * @cfg {String} icon Box icon
23674  * @cfg {String} footer Footer text
23675  * @cfg {String} fhref Footer href
23676  * 
23677  * @constructor
23678  * Create a new NumberBox
23679  * @param {Object} config The config object
23680  */
23681
23682
23683 Roo.bootstrap.dash.NumberBox = function(config){
23684     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23685     
23686 };
23687
23688 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23689     
23690     headline : '',
23691     content : '',
23692     icon : '',
23693     footer : '',
23694     fhref : '',
23695     ficon : '',
23696     
23697     getAutoCreate : function(){
23698         
23699         var cfg = {
23700             tag : 'div',
23701             cls : 'small-box ',
23702             cn : [
23703                 {
23704                     tag : 'div',
23705                     cls : 'inner',
23706                     cn :[
23707                         {
23708                             tag : 'h3',
23709                             cls : 'roo-headline',
23710                             html : this.headline
23711                         },
23712                         {
23713                             tag : 'p',
23714                             cls : 'roo-content',
23715                             html : this.content
23716                         }
23717                     ]
23718                 }
23719             ]
23720         };
23721         
23722         if(this.icon){
23723             cfg.cn.push({
23724                 tag : 'div',
23725                 cls : 'icon',
23726                 cn :[
23727                     {
23728                         tag : 'i',
23729                         cls : 'ion ' + this.icon
23730                     }
23731                 ]
23732             });
23733         }
23734         
23735         if(this.footer){
23736             var footer = {
23737                 tag : 'a',
23738                 cls : 'small-box-footer',
23739                 href : this.fhref || '#',
23740                 html : this.footer
23741             };
23742             
23743             cfg.cn.push(footer);
23744             
23745         }
23746         
23747         return  cfg;
23748     },
23749
23750     onRender : function(ct,position){
23751         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23752
23753
23754        
23755                 
23756     },
23757
23758     setHeadline: function (value)
23759     {
23760         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23761     },
23762     
23763     setFooter: function (value, href)
23764     {
23765         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23766         
23767         if(href){
23768             this.el.select('a.small-box-footer',true).first().attr('href', href);
23769         }
23770         
23771     },
23772
23773     setContent: function (value)
23774     {
23775         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23776     },
23777
23778     initEvents: function() 
23779     {   
23780         
23781     }
23782     
23783 });
23784
23785  
23786 /*
23787  * - LGPL
23788  *
23789  * TabBox
23790  * 
23791  */
23792 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23793
23794 /**
23795  * @class Roo.bootstrap.dash.TabBox
23796  * @extends Roo.bootstrap.Component
23797  * Bootstrap TabBox class
23798  * @cfg {String} title Title of the TabBox
23799  * @cfg {String} icon Icon of the TabBox
23800  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23801  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23802  * 
23803  * @constructor
23804  * Create a new TabBox
23805  * @param {Object} config The config object
23806  */
23807
23808
23809 Roo.bootstrap.dash.TabBox = function(config){
23810     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23811     this.addEvents({
23812         // raw events
23813         /**
23814          * @event addpane
23815          * When a pane is added
23816          * @param {Roo.bootstrap.dash.TabPane} pane
23817          */
23818         "addpane" : true,
23819         /**
23820          * @event activatepane
23821          * When a pane is activated
23822          * @param {Roo.bootstrap.dash.TabPane} pane
23823          */
23824         "activatepane" : true
23825         
23826          
23827     });
23828     
23829     this.panes = [];
23830 };
23831
23832 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23833
23834     title : '',
23835     icon : false,
23836     showtabs : true,
23837     tabScrollable : false,
23838     
23839     getChildContainer : function()
23840     {
23841         return this.el.select('.tab-content', true).first();
23842     },
23843     
23844     getAutoCreate : function(){
23845         
23846         var header = {
23847             tag: 'li',
23848             cls: 'pull-left header',
23849             html: this.title,
23850             cn : []
23851         };
23852         
23853         if(this.icon){
23854             header.cn.push({
23855                 tag: 'i',
23856                 cls: 'fa ' + this.icon
23857             });
23858         }
23859         
23860         var h = {
23861             tag: 'ul',
23862             cls: 'nav nav-tabs pull-right',
23863             cn: [
23864                 header
23865             ]
23866         };
23867         
23868         if(this.tabScrollable){
23869             h = {
23870                 tag: 'div',
23871                 cls: 'tab-header',
23872                 cn: [
23873                     {
23874                         tag: 'ul',
23875                         cls: 'nav nav-tabs pull-right',
23876                         cn: [
23877                             header
23878                         ]
23879                     }
23880                 ]
23881             };
23882         }
23883         
23884         var cfg = {
23885             tag: 'div',
23886             cls: 'nav-tabs-custom',
23887             cn: [
23888                 h,
23889                 {
23890                     tag: 'div',
23891                     cls: 'tab-content no-padding',
23892                     cn: []
23893                 }
23894             ]
23895         };
23896
23897         return  cfg;
23898     },
23899     initEvents : function()
23900     {
23901         //Roo.log('add add pane handler');
23902         this.on('addpane', this.onAddPane, this);
23903     },
23904      /**
23905      * Updates the box title
23906      * @param {String} html to set the title to.
23907      */
23908     setTitle : function(value)
23909     {
23910         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23911     },
23912     onAddPane : function(pane)
23913     {
23914         this.panes.push(pane);
23915         //Roo.log('addpane');
23916         //Roo.log(pane);
23917         // tabs are rendere left to right..
23918         if(!this.showtabs){
23919             return;
23920         }
23921         
23922         var ctr = this.el.select('.nav-tabs', true).first();
23923          
23924          
23925         var existing = ctr.select('.nav-tab',true);
23926         var qty = existing.getCount();;
23927         
23928         
23929         var tab = ctr.createChild({
23930             tag : 'li',
23931             cls : 'nav-tab' + (qty ? '' : ' active'),
23932             cn : [
23933                 {
23934                     tag : 'a',
23935                     href:'#',
23936                     html : pane.title
23937                 }
23938             ]
23939         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23940         pane.tab = tab;
23941         
23942         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23943         if (!qty) {
23944             pane.el.addClass('active');
23945         }
23946         
23947                 
23948     },
23949     onTabClick : function(ev,un,ob,pane)
23950     {
23951         //Roo.log('tab - prev default');
23952         ev.preventDefault();
23953         
23954         
23955         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23956         pane.tab.addClass('active');
23957         //Roo.log(pane.title);
23958         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23959         // technically we should have a deactivate event.. but maybe add later.
23960         // and it should not de-activate the selected tab...
23961         this.fireEvent('activatepane', pane);
23962         pane.el.addClass('active');
23963         pane.fireEvent('activate');
23964         
23965         
23966     },
23967     
23968     getActivePane : function()
23969     {
23970         var r = false;
23971         Roo.each(this.panes, function(p) {
23972             if(p.el.hasClass('active')){
23973                 r = p;
23974                 return false;
23975             }
23976             
23977             return;
23978         });
23979         
23980         return r;
23981     }
23982     
23983     
23984 });
23985
23986  
23987 /*
23988  * - LGPL
23989  *
23990  * Tab pane
23991  * 
23992  */
23993 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23994 /**
23995  * @class Roo.bootstrap.TabPane
23996  * @extends Roo.bootstrap.Component
23997  * Bootstrap TabPane class
23998  * @cfg {Boolean} active (false | true) Default false
23999  * @cfg {String} title title of panel
24000
24001  * 
24002  * @constructor
24003  * Create a new TabPane
24004  * @param {Object} config The config object
24005  */
24006
24007 Roo.bootstrap.dash.TabPane = function(config){
24008     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24009     
24010     this.addEvents({
24011         // raw events
24012         /**
24013          * @event activate
24014          * When a pane is activated
24015          * @param {Roo.bootstrap.dash.TabPane} pane
24016          */
24017         "activate" : true
24018          
24019     });
24020 };
24021
24022 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24023     
24024     active : false,
24025     title : '',
24026     
24027     // the tabBox that this is attached to.
24028     tab : false,
24029      
24030     getAutoCreate : function() 
24031     {
24032         var cfg = {
24033             tag: 'div',
24034             cls: 'tab-pane'
24035         };
24036         
24037         if(this.active){
24038             cfg.cls += ' active';
24039         }
24040         
24041         return cfg;
24042     },
24043     initEvents  : function()
24044     {
24045         //Roo.log('trigger add pane handler');
24046         this.parent().fireEvent('addpane', this)
24047     },
24048     
24049      /**
24050      * Updates the tab title 
24051      * @param {String} html to set the title to.
24052      */
24053     setTitle: function(str)
24054     {
24055         if (!this.tab) {
24056             return;
24057         }
24058         this.title = str;
24059         this.tab.select('a', true).first().dom.innerHTML = str;
24060         
24061     }
24062     
24063     
24064     
24065 });
24066
24067  
24068
24069
24070  /*
24071  * - LGPL
24072  *
24073  * menu
24074  * 
24075  */
24076 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24077
24078 /**
24079  * @class Roo.bootstrap.menu.Menu
24080  * @extends Roo.bootstrap.Component
24081  * Bootstrap Menu class - container for Menu
24082  * @cfg {String} html Text of the menu
24083  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24084  * @cfg {String} icon Font awesome icon
24085  * @cfg {String} pos Menu align to (top | bottom) default bottom
24086  * 
24087  * 
24088  * @constructor
24089  * Create a new Menu
24090  * @param {Object} config The config object
24091  */
24092
24093
24094 Roo.bootstrap.menu.Menu = function(config){
24095     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24096     
24097     this.addEvents({
24098         /**
24099          * @event beforeshow
24100          * Fires before this menu is displayed
24101          * @param {Roo.bootstrap.menu.Menu} this
24102          */
24103         beforeshow : true,
24104         /**
24105          * @event beforehide
24106          * Fires before this menu is hidden
24107          * @param {Roo.bootstrap.menu.Menu} this
24108          */
24109         beforehide : true,
24110         /**
24111          * @event show
24112          * Fires after this menu is displayed
24113          * @param {Roo.bootstrap.menu.Menu} this
24114          */
24115         show : true,
24116         /**
24117          * @event hide
24118          * Fires after this menu is hidden
24119          * @param {Roo.bootstrap.menu.Menu} this
24120          */
24121         hide : true,
24122         /**
24123          * @event click
24124          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24125          * @param {Roo.bootstrap.menu.Menu} this
24126          * @param {Roo.EventObject} e
24127          */
24128         click : true
24129     });
24130     
24131 };
24132
24133 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24134     
24135     submenu : false,
24136     html : '',
24137     weight : 'default',
24138     icon : false,
24139     pos : 'bottom',
24140     
24141     
24142     getChildContainer : function() {
24143         if(this.isSubMenu){
24144             return this.el;
24145         }
24146         
24147         return this.el.select('ul.dropdown-menu', true).first();  
24148     },
24149     
24150     getAutoCreate : function()
24151     {
24152         var text = [
24153             {
24154                 tag : 'span',
24155                 cls : 'roo-menu-text',
24156                 html : this.html
24157             }
24158         ];
24159         
24160         if(this.icon){
24161             text.unshift({
24162                 tag : 'i',
24163                 cls : 'fa ' + this.icon
24164             })
24165         }
24166         
24167         
24168         var cfg = {
24169             tag : 'div',
24170             cls : 'btn-group',
24171             cn : [
24172                 {
24173                     tag : 'button',
24174                     cls : 'dropdown-button btn btn-' + this.weight,
24175                     cn : text
24176                 },
24177                 {
24178                     tag : 'button',
24179                     cls : 'dropdown-toggle btn btn-' + this.weight,
24180                     cn : [
24181                         {
24182                             tag : 'span',
24183                             cls : 'caret'
24184                         }
24185                     ]
24186                 },
24187                 {
24188                     tag : 'ul',
24189                     cls : 'dropdown-menu'
24190                 }
24191             ]
24192             
24193         };
24194         
24195         if(this.pos == 'top'){
24196             cfg.cls += ' dropup';
24197         }
24198         
24199         if(this.isSubMenu){
24200             cfg = {
24201                 tag : 'ul',
24202                 cls : 'dropdown-menu'
24203             }
24204         }
24205         
24206         return cfg;
24207     },
24208     
24209     onRender : function(ct, position)
24210     {
24211         this.isSubMenu = ct.hasClass('dropdown-submenu');
24212         
24213         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24214     },
24215     
24216     initEvents : function() 
24217     {
24218         if(this.isSubMenu){
24219             return;
24220         }
24221         
24222         this.hidden = true;
24223         
24224         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24225         this.triggerEl.on('click', this.onTriggerPress, this);
24226         
24227         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24228         this.buttonEl.on('click', this.onClick, this);
24229         
24230     },
24231     
24232     list : function()
24233     {
24234         if(this.isSubMenu){
24235             return this.el;
24236         }
24237         
24238         return this.el.select('ul.dropdown-menu', true).first();
24239     },
24240     
24241     onClick : function(e)
24242     {
24243         this.fireEvent("click", this, e);
24244     },
24245     
24246     onTriggerPress  : function(e)
24247     {   
24248         if (this.isVisible()) {
24249             this.hide();
24250         } else {
24251             this.show();
24252         }
24253     },
24254     
24255     isVisible : function(){
24256         return !this.hidden;
24257     },
24258     
24259     show : function()
24260     {
24261         this.fireEvent("beforeshow", this);
24262         
24263         this.hidden = false;
24264         this.el.addClass('open');
24265         
24266         Roo.get(document).on("mouseup", this.onMouseUp, this);
24267         
24268         this.fireEvent("show", this);
24269         
24270         
24271     },
24272     
24273     hide : function()
24274     {
24275         this.fireEvent("beforehide", this);
24276         
24277         this.hidden = true;
24278         this.el.removeClass('open');
24279         
24280         Roo.get(document).un("mouseup", this.onMouseUp);
24281         
24282         this.fireEvent("hide", this);
24283     },
24284     
24285     onMouseUp : function()
24286     {
24287         this.hide();
24288     }
24289     
24290 });
24291
24292  
24293  /*
24294  * - LGPL
24295  *
24296  * menu item
24297  * 
24298  */
24299 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24300
24301 /**
24302  * @class Roo.bootstrap.menu.Item
24303  * @extends Roo.bootstrap.Component
24304  * Bootstrap MenuItem class
24305  * @cfg {Boolean} submenu (true | false) default false
24306  * @cfg {String} html text of the item
24307  * @cfg {String} href the link
24308  * @cfg {Boolean} disable (true | false) default false
24309  * @cfg {Boolean} preventDefault (true | false) default true
24310  * @cfg {String} icon Font awesome icon
24311  * @cfg {String} pos Submenu align to (left | right) default right 
24312  * 
24313  * 
24314  * @constructor
24315  * Create a new Item
24316  * @param {Object} config The config object
24317  */
24318
24319
24320 Roo.bootstrap.menu.Item = function(config){
24321     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24322     this.addEvents({
24323         /**
24324          * @event mouseover
24325          * Fires when the mouse is hovering over this menu
24326          * @param {Roo.bootstrap.menu.Item} this
24327          * @param {Roo.EventObject} e
24328          */
24329         mouseover : true,
24330         /**
24331          * @event mouseout
24332          * Fires when the mouse exits this menu
24333          * @param {Roo.bootstrap.menu.Item} this
24334          * @param {Roo.EventObject} e
24335          */
24336         mouseout : true,
24337         // raw events
24338         /**
24339          * @event click
24340          * The raw click event for the entire grid.
24341          * @param {Roo.EventObject} e
24342          */
24343         click : true
24344     });
24345 };
24346
24347 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24348     
24349     submenu : false,
24350     href : '',
24351     html : '',
24352     preventDefault: true,
24353     disable : false,
24354     icon : false,
24355     pos : 'right',
24356     
24357     getAutoCreate : function()
24358     {
24359         var text = [
24360             {
24361                 tag : 'span',
24362                 cls : 'roo-menu-item-text',
24363                 html : this.html
24364             }
24365         ];
24366         
24367         if(this.icon){
24368             text.unshift({
24369                 tag : 'i',
24370                 cls : 'fa ' + this.icon
24371             })
24372         }
24373         
24374         var cfg = {
24375             tag : 'li',
24376             cn : [
24377                 {
24378                     tag : 'a',
24379                     href : this.href || '#',
24380                     cn : text
24381                 }
24382             ]
24383         };
24384         
24385         if(this.disable){
24386             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24387         }
24388         
24389         if(this.submenu){
24390             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24391             
24392             if(this.pos == 'left'){
24393                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24394             }
24395         }
24396         
24397         return cfg;
24398     },
24399     
24400     initEvents : function() 
24401     {
24402         this.el.on('mouseover', this.onMouseOver, this);
24403         this.el.on('mouseout', this.onMouseOut, this);
24404         
24405         this.el.select('a', true).first().on('click', this.onClick, this);
24406         
24407     },
24408     
24409     onClick : function(e)
24410     {
24411         if(this.preventDefault){
24412             e.preventDefault();
24413         }
24414         
24415         this.fireEvent("click", this, e);
24416     },
24417     
24418     onMouseOver : function(e)
24419     {
24420         if(this.submenu && this.pos == 'left'){
24421             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24422         }
24423         
24424         this.fireEvent("mouseover", this, e);
24425     },
24426     
24427     onMouseOut : function(e)
24428     {
24429         this.fireEvent("mouseout", this, e);
24430     }
24431 });
24432
24433  
24434
24435  /*
24436  * - LGPL
24437  *
24438  * menu separator
24439  * 
24440  */
24441 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24442
24443 /**
24444  * @class Roo.bootstrap.menu.Separator
24445  * @extends Roo.bootstrap.Component
24446  * Bootstrap Separator class
24447  * 
24448  * @constructor
24449  * Create a new Separator
24450  * @param {Object} config The config object
24451  */
24452
24453
24454 Roo.bootstrap.menu.Separator = function(config){
24455     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24456 };
24457
24458 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24459     
24460     getAutoCreate : function(){
24461         var cfg = {
24462             tag : 'li',
24463             cls: 'divider'
24464         };
24465         
24466         return cfg;
24467     }
24468    
24469 });
24470
24471  
24472
24473  /*
24474  * - LGPL
24475  *
24476  * Tooltip
24477  * 
24478  */
24479
24480 /**
24481  * @class Roo.bootstrap.Tooltip
24482  * Bootstrap Tooltip class
24483  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24484  * to determine which dom element triggers the tooltip.
24485  * 
24486  * It needs to add support for additional attributes like tooltip-position
24487  * 
24488  * @constructor
24489  * Create a new Toolti
24490  * @param {Object} config The config object
24491  */
24492
24493 Roo.bootstrap.Tooltip = function(config){
24494     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24495 };
24496
24497 Roo.apply(Roo.bootstrap.Tooltip, {
24498     /**
24499      * @function init initialize tooltip monitoring.
24500      * @static
24501      */
24502     currentEl : false,
24503     currentTip : false,
24504     currentRegion : false,
24505     
24506     //  init : delay?
24507     
24508     init : function()
24509     {
24510         Roo.get(document).on('mouseover', this.enter ,this);
24511         Roo.get(document).on('mouseout', this.leave, this);
24512          
24513         
24514         this.currentTip = new Roo.bootstrap.Tooltip();
24515     },
24516     
24517     enter : function(ev)
24518     {
24519         var dom = ev.getTarget();
24520         
24521         //Roo.log(['enter',dom]);
24522         var el = Roo.fly(dom);
24523         if (this.currentEl) {
24524             //Roo.log(dom);
24525             //Roo.log(this.currentEl);
24526             //Roo.log(this.currentEl.contains(dom));
24527             if (this.currentEl == el) {
24528                 return;
24529             }
24530             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24531                 return;
24532             }
24533
24534         }
24535         
24536         if (this.currentTip.el) {
24537             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24538         }    
24539         //Roo.log(ev);
24540         
24541         if(!el || el.dom == document){
24542             return;
24543         }
24544         
24545         var bindEl = el;
24546         
24547         // you can not look for children, as if el is the body.. then everythign is the child..
24548         if (!el.attr('tooltip')) { //
24549             if (!el.select("[tooltip]").elements.length) {
24550                 return;
24551             }
24552             // is the mouse over this child...?
24553             bindEl = el.select("[tooltip]").first();
24554             var xy = ev.getXY();
24555             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24556                 //Roo.log("not in region.");
24557                 return;
24558             }
24559             //Roo.log("child element over..");
24560             
24561         }
24562         this.currentEl = bindEl;
24563         this.currentTip.bind(bindEl);
24564         this.currentRegion = Roo.lib.Region.getRegion(dom);
24565         this.currentTip.enter();
24566         
24567     },
24568     leave : function(ev)
24569     {
24570         var dom = ev.getTarget();
24571         //Roo.log(['leave',dom]);
24572         if (!this.currentEl) {
24573             return;
24574         }
24575         
24576         
24577         if (dom != this.currentEl.dom) {
24578             return;
24579         }
24580         var xy = ev.getXY();
24581         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24582             return;
24583         }
24584         // only activate leave if mouse cursor is outside... bounding box..
24585         
24586         
24587         
24588         
24589         if (this.currentTip) {
24590             this.currentTip.leave();
24591         }
24592         //Roo.log('clear currentEl');
24593         this.currentEl = false;
24594         
24595         
24596     },
24597     alignment : {
24598         'left' : ['r-l', [-2,0], 'right'],
24599         'right' : ['l-r', [2,0], 'left'],
24600         'bottom' : ['t-b', [0,2], 'top'],
24601         'top' : [ 'b-t', [0,-2], 'bottom']
24602     }
24603     
24604 });
24605
24606
24607 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24608     
24609     
24610     bindEl : false,
24611     
24612     delay : null, // can be { show : 300 , hide: 500}
24613     
24614     timeout : null,
24615     
24616     hoverState : null, //???
24617     
24618     placement : 'bottom', 
24619     
24620     getAutoCreate : function(){
24621     
24622         var cfg = {
24623            cls : 'tooltip',
24624            role : 'tooltip',
24625            cn : [
24626                 {
24627                     cls : 'tooltip-arrow'
24628                 },
24629                 {
24630                     cls : 'tooltip-inner'
24631                 }
24632            ]
24633         };
24634         
24635         return cfg;
24636     },
24637     bind : function(el)
24638     {
24639         this.bindEl = el;
24640     },
24641       
24642     
24643     enter : function () {
24644        
24645         if (this.timeout != null) {
24646             clearTimeout(this.timeout);
24647         }
24648         
24649         this.hoverState = 'in';
24650          //Roo.log("enter - show");
24651         if (!this.delay || !this.delay.show) {
24652             this.show();
24653             return;
24654         }
24655         var _t = this;
24656         this.timeout = setTimeout(function () {
24657             if (_t.hoverState == 'in') {
24658                 _t.show();
24659             }
24660         }, this.delay.show);
24661     },
24662     leave : function()
24663     {
24664         clearTimeout(this.timeout);
24665     
24666         this.hoverState = 'out';
24667          if (!this.delay || !this.delay.hide) {
24668             this.hide();
24669             return;
24670         }
24671        
24672         var _t = this;
24673         this.timeout = setTimeout(function () {
24674             //Roo.log("leave - timeout");
24675             
24676             if (_t.hoverState == 'out') {
24677                 _t.hide();
24678                 Roo.bootstrap.Tooltip.currentEl = false;
24679             }
24680         }, delay);
24681     },
24682     
24683     show : function ()
24684     {
24685         if (!this.el) {
24686             this.render(document.body);
24687         }
24688         // set content.
24689         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24690         
24691         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24692         
24693         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24694         
24695         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24696         
24697         var placement = typeof this.placement == 'function' ?
24698             this.placement.call(this, this.el, on_el) :
24699             this.placement;
24700             
24701         var autoToken = /\s?auto?\s?/i;
24702         var autoPlace = autoToken.test(placement);
24703         if (autoPlace) {
24704             placement = placement.replace(autoToken, '') || 'top';
24705         }
24706         
24707         //this.el.detach()
24708         //this.el.setXY([0,0]);
24709         this.el.show();
24710         //this.el.dom.style.display='block';
24711         
24712         //this.el.appendTo(on_el);
24713         
24714         var p = this.getPosition();
24715         var box = this.el.getBox();
24716         
24717         if (autoPlace) {
24718             // fixme..
24719         }
24720         
24721         var align = Roo.bootstrap.Tooltip.alignment[placement];
24722         
24723         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24724         
24725         if(placement == 'top' || placement == 'bottom'){
24726             if(xy[0] < 0){
24727                 placement = 'right';
24728             }
24729             
24730             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24731                 placement = 'left';
24732             }
24733             
24734             var scroll = Roo.select('body', true).first().getScroll();
24735             
24736             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24737                 placement = 'top';
24738             }
24739             
24740         }
24741         
24742         align = Roo.bootstrap.Tooltip.alignment[placement];
24743         
24744         this.el.alignTo(this.bindEl, align[0],align[1]);
24745         //var arrow = this.el.select('.arrow',true).first();
24746         //arrow.set(align[2], 
24747         
24748         this.el.addClass(placement);
24749         
24750         this.el.addClass('in fade');
24751         
24752         this.hoverState = null;
24753         
24754         if (this.el.hasClass('fade')) {
24755             // fade it?
24756         }
24757         
24758     },
24759     hide : function()
24760     {
24761          
24762         if (!this.el) {
24763             return;
24764         }
24765         //this.el.setXY([0,0]);
24766         this.el.removeClass('in');
24767         //this.el.hide();
24768         
24769     }
24770     
24771 });
24772  
24773
24774  /*
24775  * - LGPL
24776  *
24777  * Location Picker
24778  * 
24779  */
24780
24781 /**
24782  * @class Roo.bootstrap.LocationPicker
24783  * @extends Roo.bootstrap.Component
24784  * Bootstrap LocationPicker class
24785  * @cfg {Number} latitude Position when init default 0
24786  * @cfg {Number} longitude Position when init default 0
24787  * @cfg {Number} zoom default 15
24788  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24789  * @cfg {Boolean} mapTypeControl default false
24790  * @cfg {Boolean} disableDoubleClickZoom default false
24791  * @cfg {Boolean} scrollwheel default true
24792  * @cfg {Boolean} streetViewControl default false
24793  * @cfg {Number} radius default 0
24794  * @cfg {String} locationName
24795  * @cfg {Boolean} draggable default true
24796  * @cfg {Boolean} enableAutocomplete default false
24797  * @cfg {Boolean} enableReverseGeocode default true
24798  * @cfg {String} markerTitle
24799  * 
24800  * @constructor
24801  * Create a new LocationPicker
24802  * @param {Object} config The config object
24803  */
24804
24805
24806 Roo.bootstrap.LocationPicker = function(config){
24807     
24808     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24809     
24810     this.addEvents({
24811         /**
24812          * @event initial
24813          * Fires when the picker initialized.
24814          * @param {Roo.bootstrap.LocationPicker} this
24815          * @param {Google Location} location
24816          */
24817         initial : true,
24818         /**
24819          * @event positionchanged
24820          * Fires when the picker position changed.
24821          * @param {Roo.bootstrap.LocationPicker} this
24822          * @param {Google Location} location
24823          */
24824         positionchanged : true,
24825         /**
24826          * @event resize
24827          * Fires when the map resize.
24828          * @param {Roo.bootstrap.LocationPicker} this
24829          */
24830         resize : true,
24831         /**
24832          * @event show
24833          * Fires when the map show.
24834          * @param {Roo.bootstrap.LocationPicker} this
24835          */
24836         show : true,
24837         /**
24838          * @event hide
24839          * Fires when the map hide.
24840          * @param {Roo.bootstrap.LocationPicker} this
24841          */
24842         hide : true,
24843         /**
24844          * @event mapClick
24845          * Fires when click the map.
24846          * @param {Roo.bootstrap.LocationPicker} this
24847          * @param {Map event} e
24848          */
24849         mapClick : true,
24850         /**
24851          * @event mapRightClick
24852          * Fires when right click the map.
24853          * @param {Roo.bootstrap.LocationPicker} this
24854          * @param {Map event} e
24855          */
24856         mapRightClick : true,
24857         /**
24858          * @event markerClick
24859          * Fires when click the marker.
24860          * @param {Roo.bootstrap.LocationPicker} this
24861          * @param {Map event} e
24862          */
24863         markerClick : true,
24864         /**
24865          * @event markerRightClick
24866          * Fires when right click the marker.
24867          * @param {Roo.bootstrap.LocationPicker} this
24868          * @param {Map event} e
24869          */
24870         markerRightClick : true,
24871         /**
24872          * @event OverlayViewDraw
24873          * Fires when OverlayView Draw
24874          * @param {Roo.bootstrap.LocationPicker} this
24875          */
24876         OverlayViewDraw : true,
24877         /**
24878          * @event OverlayViewOnAdd
24879          * Fires when OverlayView Draw
24880          * @param {Roo.bootstrap.LocationPicker} this
24881          */
24882         OverlayViewOnAdd : true,
24883         /**
24884          * @event OverlayViewOnRemove
24885          * Fires when OverlayView Draw
24886          * @param {Roo.bootstrap.LocationPicker} this
24887          */
24888         OverlayViewOnRemove : true,
24889         /**
24890          * @event OverlayViewShow
24891          * Fires when OverlayView Draw
24892          * @param {Roo.bootstrap.LocationPicker} this
24893          * @param {Pixel} cpx
24894          */
24895         OverlayViewShow : true,
24896         /**
24897          * @event OverlayViewHide
24898          * Fires when OverlayView Draw
24899          * @param {Roo.bootstrap.LocationPicker} this
24900          */
24901         OverlayViewHide : true,
24902         /**
24903          * @event loadexception
24904          * Fires when load google lib failed.
24905          * @param {Roo.bootstrap.LocationPicker} this
24906          */
24907         loadexception : true
24908     });
24909         
24910 };
24911
24912 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24913     
24914     gMapContext: false,
24915     
24916     latitude: 0,
24917     longitude: 0,
24918     zoom: 15,
24919     mapTypeId: false,
24920     mapTypeControl: false,
24921     disableDoubleClickZoom: false,
24922     scrollwheel: true,
24923     streetViewControl: false,
24924     radius: 0,
24925     locationName: '',
24926     draggable: true,
24927     enableAutocomplete: false,
24928     enableReverseGeocode: true,
24929     markerTitle: '',
24930     
24931     getAutoCreate: function()
24932     {
24933
24934         var cfg = {
24935             tag: 'div',
24936             cls: 'roo-location-picker'
24937         };
24938         
24939         return cfg
24940     },
24941     
24942     initEvents: function(ct, position)
24943     {       
24944         if(!this.el.getWidth() || this.isApplied()){
24945             return;
24946         }
24947         
24948         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24949         
24950         this.initial();
24951     },
24952     
24953     initial: function()
24954     {
24955         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24956             this.fireEvent('loadexception', this);
24957             return;
24958         }
24959         
24960         if(!this.mapTypeId){
24961             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24962         }
24963         
24964         this.gMapContext = this.GMapContext();
24965         
24966         this.initOverlayView();
24967         
24968         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24969         
24970         var _this = this;
24971                 
24972         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24973             _this.setPosition(_this.gMapContext.marker.position);
24974         });
24975         
24976         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24977             _this.fireEvent('mapClick', this, event);
24978             
24979         });
24980
24981         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24982             _this.fireEvent('mapRightClick', this, event);
24983             
24984         });
24985         
24986         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24987             _this.fireEvent('markerClick', this, event);
24988             
24989         });
24990
24991         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24992             _this.fireEvent('markerRightClick', this, event);
24993             
24994         });
24995         
24996         this.setPosition(this.gMapContext.location);
24997         
24998         this.fireEvent('initial', this, this.gMapContext.location);
24999     },
25000     
25001     initOverlayView: function()
25002     {
25003         var _this = this;
25004         
25005         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25006             
25007             draw: function()
25008             {
25009                 _this.fireEvent('OverlayViewDraw', _this);
25010             },
25011             
25012             onAdd: function()
25013             {
25014                 _this.fireEvent('OverlayViewOnAdd', _this);
25015             },
25016             
25017             onRemove: function()
25018             {
25019                 _this.fireEvent('OverlayViewOnRemove', _this);
25020             },
25021             
25022             show: function(cpx)
25023             {
25024                 _this.fireEvent('OverlayViewShow', _this, cpx);
25025             },
25026             
25027             hide: function()
25028             {
25029                 _this.fireEvent('OverlayViewHide', _this);
25030             }
25031             
25032         });
25033     },
25034     
25035     fromLatLngToContainerPixel: function(event)
25036     {
25037         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25038     },
25039     
25040     isApplied: function() 
25041     {
25042         return this.getGmapContext() == false ? false : true;
25043     },
25044     
25045     getGmapContext: function() 
25046     {
25047         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25048     },
25049     
25050     GMapContext: function() 
25051     {
25052         var position = new google.maps.LatLng(this.latitude, this.longitude);
25053         
25054         var _map = new google.maps.Map(this.el.dom, {
25055             center: position,
25056             zoom: this.zoom,
25057             mapTypeId: this.mapTypeId,
25058             mapTypeControl: this.mapTypeControl,
25059             disableDoubleClickZoom: this.disableDoubleClickZoom,
25060             scrollwheel: this.scrollwheel,
25061             streetViewControl: this.streetViewControl,
25062             locationName: this.locationName,
25063             draggable: this.draggable,
25064             enableAutocomplete: this.enableAutocomplete,
25065             enableReverseGeocode: this.enableReverseGeocode
25066         });
25067         
25068         var _marker = new google.maps.Marker({
25069             position: position,
25070             map: _map,
25071             title: this.markerTitle,
25072             draggable: this.draggable
25073         });
25074         
25075         return {
25076             map: _map,
25077             marker: _marker,
25078             circle: null,
25079             location: position,
25080             radius: this.radius,
25081             locationName: this.locationName,
25082             addressComponents: {
25083                 formatted_address: null,
25084                 addressLine1: null,
25085                 addressLine2: null,
25086                 streetName: null,
25087                 streetNumber: null,
25088                 city: null,
25089                 district: null,
25090                 state: null,
25091                 stateOrProvince: null
25092             },
25093             settings: this,
25094             domContainer: this.el.dom,
25095             geodecoder: new google.maps.Geocoder()
25096         };
25097     },
25098     
25099     drawCircle: function(center, radius, options) 
25100     {
25101         if (this.gMapContext.circle != null) {
25102             this.gMapContext.circle.setMap(null);
25103         }
25104         if (radius > 0) {
25105             radius *= 1;
25106             options = Roo.apply({}, options, {
25107                 strokeColor: "#0000FF",
25108                 strokeOpacity: .35,
25109                 strokeWeight: 2,
25110                 fillColor: "#0000FF",
25111                 fillOpacity: .2
25112             });
25113             
25114             options.map = this.gMapContext.map;
25115             options.radius = radius;
25116             options.center = center;
25117             this.gMapContext.circle = new google.maps.Circle(options);
25118             return this.gMapContext.circle;
25119         }
25120         
25121         return null;
25122     },
25123     
25124     setPosition: function(location) 
25125     {
25126         this.gMapContext.location = location;
25127         this.gMapContext.marker.setPosition(location);
25128         this.gMapContext.map.panTo(location);
25129         this.drawCircle(location, this.gMapContext.radius, {});
25130         
25131         var _this = this;
25132         
25133         if (this.gMapContext.settings.enableReverseGeocode) {
25134             this.gMapContext.geodecoder.geocode({
25135                 latLng: this.gMapContext.location
25136             }, function(results, status) {
25137                 
25138                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25139                     _this.gMapContext.locationName = results[0].formatted_address;
25140                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25141                     
25142                     _this.fireEvent('positionchanged', this, location);
25143                 }
25144             });
25145             
25146             return;
25147         }
25148         
25149         this.fireEvent('positionchanged', this, location);
25150     },
25151     
25152     resize: function()
25153     {
25154         google.maps.event.trigger(this.gMapContext.map, "resize");
25155         
25156         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25157         
25158         this.fireEvent('resize', this);
25159     },
25160     
25161     setPositionByLatLng: function(latitude, longitude)
25162     {
25163         this.setPosition(new google.maps.LatLng(latitude, longitude));
25164     },
25165     
25166     getCurrentPosition: function() 
25167     {
25168         return {
25169             latitude: this.gMapContext.location.lat(),
25170             longitude: this.gMapContext.location.lng()
25171         };
25172     },
25173     
25174     getAddressName: function() 
25175     {
25176         return this.gMapContext.locationName;
25177     },
25178     
25179     getAddressComponents: function() 
25180     {
25181         return this.gMapContext.addressComponents;
25182     },
25183     
25184     address_component_from_google_geocode: function(address_components) 
25185     {
25186         var result = {};
25187         
25188         for (var i = 0; i < address_components.length; i++) {
25189             var component = address_components[i];
25190             if (component.types.indexOf("postal_code") >= 0) {
25191                 result.postalCode = component.short_name;
25192             } else if (component.types.indexOf("street_number") >= 0) {
25193                 result.streetNumber = component.short_name;
25194             } else if (component.types.indexOf("route") >= 0) {
25195                 result.streetName = component.short_name;
25196             } else if (component.types.indexOf("neighborhood") >= 0) {
25197                 result.city = component.short_name;
25198             } else if (component.types.indexOf("locality") >= 0) {
25199                 result.city = component.short_name;
25200             } else if (component.types.indexOf("sublocality") >= 0) {
25201                 result.district = component.short_name;
25202             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25203                 result.stateOrProvince = component.short_name;
25204             } else if (component.types.indexOf("country") >= 0) {
25205                 result.country = component.short_name;
25206             }
25207         }
25208         
25209         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25210         result.addressLine2 = "";
25211         return result;
25212     },
25213     
25214     setZoomLevel: function(zoom)
25215     {
25216         this.gMapContext.map.setZoom(zoom);
25217     },
25218     
25219     show: function()
25220     {
25221         if(!this.el){
25222             return;
25223         }
25224         
25225         this.el.show();
25226         
25227         this.resize();
25228         
25229         this.fireEvent('show', this);
25230     },
25231     
25232     hide: function()
25233     {
25234         if(!this.el){
25235             return;
25236         }
25237         
25238         this.el.hide();
25239         
25240         this.fireEvent('hide', this);
25241     }
25242     
25243 });
25244
25245 Roo.apply(Roo.bootstrap.LocationPicker, {
25246     
25247     OverlayView : function(map, options)
25248     {
25249         options = options || {};
25250         
25251         this.setMap(map);
25252     }
25253     
25254     
25255 });/*
25256  * - LGPL
25257  *
25258  * Alert
25259  * 
25260  */
25261
25262 /**
25263  * @class Roo.bootstrap.Alert
25264  * @extends Roo.bootstrap.Component
25265  * Bootstrap Alert class
25266  * @cfg {String} title The title of alert
25267  * @cfg {String} html The content of alert
25268  * @cfg {String} weight (  success | info | warning | danger )
25269  * @cfg {String} faicon font-awesomeicon
25270  * 
25271  * @constructor
25272  * Create a new alert
25273  * @param {Object} config The config object
25274  */
25275
25276
25277 Roo.bootstrap.Alert = function(config){
25278     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25279     
25280 };
25281
25282 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25283     
25284     title: '',
25285     html: '',
25286     weight: false,
25287     faicon: false,
25288     
25289     getAutoCreate : function()
25290     {
25291         
25292         var cfg = {
25293             tag : 'div',
25294             cls : 'alert',
25295             cn : [
25296                 {
25297                     tag : 'i',
25298                     cls : 'roo-alert-icon'
25299                     
25300                 },
25301                 {
25302                     tag : 'b',
25303                     cls : 'roo-alert-title',
25304                     html : this.title
25305                 },
25306                 {
25307                     tag : 'span',
25308                     cls : 'roo-alert-text',
25309                     html : this.html
25310                 }
25311             ]
25312         };
25313         
25314         if(this.faicon){
25315             cfg.cn[0].cls += ' fa ' + this.faicon;
25316         }
25317         
25318         if(this.weight){
25319             cfg.cls += ' alert-' + this.weight;
25320         }
25321         
25322         return cfg;
25323     },
25324     
25325     initEvents: function() 
25326     {
25327         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25328     },
25329     
25330     setTitle : function(str)
25331     {
25332         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25333     },
25334     
25335     setText : function(str)
25336     {
25337         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25338     },
25339     
25340     setWeight : function(weight)
25341     {
25342         if(this.weight){
25343             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25344         }
25345         
25346         this.weight = weight;
25347         
25348         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25349     },
25350     
25351     setIcon : function(icon)
25352     {
25353         if(this.faicon){
25354             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25355         }
25356         
25357         this.faicon = icon;
25358         
25359         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25360     },
25361     
25362     hide: function() 
25363     {
25364         this.el.hide();   
25365     },
25366     
25367     show: function() 
25368     {  
25369         this.el.show();   
25370     }
25371     
25372 });
25373
25374  
25375 /*
25376 * Licence: LGPL
25377 */
25378
25379 /**
25380  * @class Roo.bootstrap.UploadCropbox
25381  * @extends Roo.bootstrap.Component
25382  * Bootstrap UploadCropbox class
25383  * @cfg {String} emptyText show when image has been loaded
25384  * @cfg {String} rotateNotify show when image too small to rotate
25385  * @cfg {Number} errorTimeout default 3000
25386  * @cfg {Number} minWidth default 300
25387  * @cfg {Number} minHeight default 300
25388  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25389  * @cfg {Boolean} isDocument (true|false) default false
25390  * @cfg {String} url action url
25391  * @cfg {String} paramName default 'imageUpload'
25392  * @cfg {String} method default POST
25393  * @cfg {Boolean} loadMask (true|false) default true
25394  * @cfg {Boolean} loadingText default 'Loading...'
25395  * 
25396  * @constructor
25397  * Create a new UploadCropbox
25398  * @param {Object} config The config object
25399  */
25400
25401 Roo.bootstrap.UploadCropbox = function(config){
25402     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25403     
25404     this.addEvents({
25405         /**
25406          * @event beforeselectfile
25407          * Fire before select file
25408          * @param {Roo.bootstrap.UploadCropbox} this
25409          */
25410         "beforeselectfile" : true,
25411         /**
25412          * @event initial
25413          * Fire after initEvent
25414          * @param {Roo.bootstrap.UploadCropbox} this
25415          */
25416         "initial" : true,
25417         /**
25418          * @event crop
25419          * Fire after initEvent
25420          * @param {Roo.bootstrap.UploadCropbox} this
25421          * @param {String} data
25422          */
25423         "crop" : true,
25424         /**
25425          * @event prepare
25426          * Fire when preparing the file data
25427          * @param {Roo.bootstrap.UploadCropbox} this
25428          * @param {Object} file
25429          */
25430         "prepare" : true,
25431         /**
25432          * @event exception
25433          * Fire when get exception
25434          * @param {Roo.bootstrap.UploadCropbox} this
25435          * @param {XMLHttpRequest} xhr
25436          */
25437         "exception" : true,
25438         /**
25439          * @event beforeloadcanvas
25440          * Fire before load the canvas
25441          * @param {Roo.bootstrap.UploadCropbox} this
25442          * @param {String} src
25443          */
25444         "beforeloadcanvas" : true,
25445         /**
25446          * @event trash
25447          * Fire when trash image
25448          * @param {Roo.bootstrap.UploadCropbox} this
25449          */
25450         "trash" : true,
25451         /**
25452          * @event download
25453          * Fire when download the image
25454          * @param {Roo.bootstrap.UploadCropbox} this
25455          */
25456         "download" : true,
25457         /**
25458          * @event footerbuttonclick
25459          * Fire when footerbuttonclick
25460          * @param {Roo.bootstrap.UploadCropbox} this
25461          * @param {String} type
25462          */
25463         "footerbuttonclick" : true,
25464         /**
25465          * @event resize
25466          * Fire when resize
25467          * @param {Roo.bootstrap.UploadCropbox} this
25468          */
25469         "resize" : true,
25470         /**
25471          * @event rotate
25472          * Fire when rotate the image
25473          * @param {Roo.bootstrap.UploadCropbox} this
25474          * @param {String} pos
25475          */
25476         "rotate" : true,
25477         /**
25478          * @event inspect
25479          * Fire when inspect the file
25480          * @param {Roo.bootstrap.UploadCropbox} this
25481          * @param {Object} file
25482          */
25483         "inspect" : true,
25484         /**
25485          * @event upload
25486          * Fire when xhr upload the file
25487          * @param {Roo.bootstrap.UploadCropbox} this
25488          * @param {Object} data
25489          */
25490         "upload" : true,
25491         /**
25492          * @event arrange
25493          * Fire when arrange the file data
25494          * @param {Roo.bootstrap.UploadCropbox} this
25495          * @param {Object} formData
25496          */
25497         "arrange" : true
25498     });
25499     
25500     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25501 };
25502
25503 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25504     
25505     emptyText : 'Click to upload image',
25506     rotateNotify : 'Image is too small to rotate',
25507     errorTimeout : 3000,
25508     scale : 0,
25509     baseScale : 1,
25510     rotate : 0,
25511     dragable : false,
25512     pinching : false,
25513     mouseX : 0,
25514     mouseY : 0,
25515     cropData : false,
25516     minWidth : 300,
25517     minHeight : 300,
25518     file : false,
25519     exif : {},
25520     baseRotate : 1,
25521     cropType : 'image/jpeg',
25522     buttons : false,
25523     canvasLoaded : false,
25524     isDocument : false,
25525     method : 'POST',
25526     paramName : 'imageUpload',
25527     loadMask : true,
25528     loadingText : 'Loading...',
25529     maskEl : false,
25530     
25531     getAutoCreate : function()
25532     {
25533         var cfg = {
25534             tag : 'div',
25535             cls : 'roo-upload-cropbox',
25536             cn : [
25537                 {
25538                     tag : 'input',
25539                     cls : 'roo-upload-cropbox-selector',
25540                     type : 'file'
25541                 },
25542                 {
25543                     tag : 'div',
25544                     cls : 'roo-upload-cropbox-body',
25545                     style : 'cursor:pointer',
25546                     cn : [
25547                         {
25548                             tag : 'div',
25549                             cls : 'roo-upload-cropbox-preview'
25550                         },
25551                         {
25552                             tag : 'div',
25553                             cls : 'roo-upload-cropbox-thumb'
25554                         },
25555                         {
25556                             tag : 'div',
25557                             cls : 'roo-upload-cropbox-empty-notify',
25558                             html : this.emptyText
25559                         },
25560                         {
25561                             tag : 'div',
25562                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25563                             html : this.rotateNotify
25564                         }
25565                     ]
25566                 },
25567                 {
25568                     tag : 'div',
25569                     cls : 'roo-upload-cropbox-footer',
25570                     cn : {
25571                         tag : 'div',
25572                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25573                         cn : []
25574                     }
25575                 }
25576             ]
25577         };
25578         
25579         return cfg;
25580     },
25581     
25582     onRender : function(ct, position)
25583     {
25584         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25585         
25586         if (this.buttons.length) {
25587             
25588             Roo.each(this.buttons, function(bb) {
25589                 
25590                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25591                 
25592                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25593                 
25594             }, this);
25595         }
25596         
25597         if(this.loadMask){
25598             this.maskEl = this.el;
25599         }
25600     },
25601     
25602     initEvents : function()
25603     {
25604         this.urlAPI = (window.createObjectURL && window) || 
25605                                 (window.URL && URL.revokeObjectURL && URL) || 
25606                                 (window.webkitURL && webkitURL);
25607                         
25608         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25609         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25610         
25611         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25612         this.selectorEl.hide();
25613         
25614         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25615         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25616         
25617         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25618         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25619         this.thumbEl.hide();
25620         
25621         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25622         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25623         
25624         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25625         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25626         this.errorEl.hide();
25627         
25628         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25629         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25630         this.footerEl.hide();
25631         
25632         this.setThumbBoxSize();
25633         
25634         this.bind();
25635         
25636         this.resize();
25637         
25638         this.fireEvent('initial', this);
25639     },
25640
25641     bind : function()
25642     {
25643         var _this = this;
25644         
25645         window.addEventListener("resize", function() { _this.resize(); } );
25646         
25647         this.bodyEl.on('click', this.beforeSelectFile, this);
25648         
25649         if(Roo.isTouch){
25650             this.bodyEl.on('touchstart', this.onTouchStart, this);
25651             this.bodyEl.on('touchmove', this.onTouchMove, this);
25652             this.bodyEl.on('touchend', this.onTouchEnd, this);
25653         }
25654         
25655         if(!Roo.isTouch){
25656             this.bodyEl.on('mousedown', this.onMouseDown, this);
25657             this.bodyEl.on('mousemove', this.onMouseMove, this);
25658             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25659             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25660             Roo.get(document).on('mouseup', this.onMouseUp, this);
25661         }
25662         
25663         this.selectorEl.on('change', this.onFileSelected, this);
25664     },
25665     
25666     reset : function()
25667     {    
25668         this.scale = 0;
25669         this.baseScale = 1;
25670         this.rotate = 0;
25671         this.baseRotate = 1;
25672         this.dragable = false;
25673         this.pinching = false;
25674         this.mouseX = 0;
25675         this.mouseY = 0;
25676         this.cropData = false;
25677         this.notifyEl.dom.innerHTML = this.emptyText;
25678         
25679         this.selectorEl.dom.value = '';
25680         
25681     },
25682     
25683     resize : function()
25684     {
25685         if(this.fireEvent('resize', this) != false){
25686             this.setThumbBoxPosition();
25687             this.setCanvasPosition();
25688         }
25689     },
25690     
25691     onFooterButtonClick : function(e, el, o, type)
25692     {
25693         switch (type) {
25694             case 'rotate-left' :
25695                 this.onRotateLeft(e);
25696                 break;
25697             case 'rotate-right' :
25698                 this.onRotateRight(e);
25699                 break;
25700             case 'picture' :
25701                 this.beforeSelectFile(e);
25702                 break;
25703             case 'trash' :
25704                 this.trash(e);
25705                 break;
25706             case 'crop' :
25707                 this.crop(e);
25708                 break;
25709             case 'download' :
25710                 this.download(e);
25711                 break;
25712             default :
25713                 break;
25714         }
25715         
25716         this.fireEvent('footerbuttonclick', this, type);
25717     },
25718     
25719     beforeSelectFile : function(e)
25720     {
25721         e.preventDefault();
25722         
25723         if(this.fireEvent('beforeselectfile', this) != false){
25724             this.selectorEl.dom.click();
25725         }
25726     },
25727     
25728     onFileSelected : function(e)
25729     {
25730         e.preventDefault();
25731         
25732         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25733             return;
25734         }
25735         
25736         var file = this.selectorEl.dom.files[0];
25737         
25738         if(this.fireEvent('inspect', this, file) != false){
25739             this.prepare(file);
25740         }
25741         
25742     },
25743     
25744     trash : function(e)
25745     {
25746         this.fireEvent('trash', this);
25747     },
25748     
25749     download : function(e)
25750     {
25751         this.fireEvent('download', this);
25752     },
25753     
25754     loadCanvas : function(src)
25755     {   
25756         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25757             
25758             this.reset();
25759             
25760             this.imageEl = document.createElement('img');
25761             
25762             var _this = this;
25763             
25764             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25765             
25766             this.imageEl.src = src;
25767         }
25768     },
25769     
25770     onLoadCanvas : function()
25771     {   
25772         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25773         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25774         
25775         this.bodyEl.un('click', this.beforeSelectFile, this);
25776         
25777         this.notifyEl.hide();
25778         this.thumbEl.show();
25779         this.footerEl.show();
25780         
25781         this.baseRotateLevel();
25782         
25783         if(this.isDocument){
25784             this.setThumbBoxSize();
25785         }
25786         
25787         this.setThumbBoxPosition();
25788         
25789         this.baseScaleLevel();
25790         
25791         this.draw();
25792         
25793         this.resize();
25794         
25795         this.canvasLoaded = true;
25796         
25797         if(this.loadMask){
25798             this.maskEl.unmask();
25799         }
25800         
25801     },
25802     
25803     setCanvasPosition : function()
25804     {   
25805         if(!this.canvasEl){
25806             return;
25807         }
25808         
25809         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25810         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25811         
25812         this.previewEl.setLeft(pw);
25813         this.previewEl.setTop(ph);
25814         
25815     },
25816     
25817     onMouseDown : function(e)
25818     {   
25819         e.stopEvent();
25820         
25821         this.dragable = true;
25822         this.pinching = false;
25823         
25824         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25825             this.dragable = false;
25826             return;
25827         }
25828         
25829         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25830         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25831         
25832     },
25833     
25834     onMouseMove : function(e)
25835     {   
25836         e.stopEvent();
25837         
25838         if(!this.canvasLoaded){
25839             return;
25840         }
25841         
25842         if (!this.dragable){
25843             return;
25844         }
25845         
25846         var minX = Math.ceil(this.thumbEl.getLeft(true));
25847         var minY = Math.ceil(this.thumbEl.getTop(true));
25848         
25849         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25850         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25851         
25852         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25853         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25854         
25855         x = x - this.mouseX;
25856         y = y - this.mouseY;
25857         
25858         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25859         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25860         
25861         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25862         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25863         
25864         this.previewEl.setLeft(bgX);
25865         this.previewEl.setTop(bgY);
25866         
25867         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25868         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25869     },
25870     
25871     onMouseUp : function(e)
25872     {   
25873         e.stopEvent();
25874         
25875         this.dragable = false;
25876     },
25877     
25878     onMouseWheel : function(e)
25879     {   
25880         e.stopEvent();
25881         
25882         this.startScale = this.scale;
25883         
25884         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25885         
25886         if(!this.zoomable()){
25887             this.scale = this.startScale;
25888             return;
25889         }
25890         
25891         this.draw();
25892         
25893         return;
25894     },
25895     
25896     zoomable : function()
25897     {
25898         var minScale = this.thumbEl.getWidth() / this.minWidth;
25899         
25900         if(this.minWidth < this.minHeight){
25901             minScale = this.thumbEl.getHeight() / this.minHeight;
25902         }
25903         
25904         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25905         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25906         
25907         if(
25908                 this.isDocument &&
25909                 (this.rotate == 0 || this.rotate == 180) && 
25910                 (
25911                     width > this.imageEl.OriginWidth || 
25912                     height > this.imageEl.OriginHeight ||
25913                     (width < this.minWidth && height < this.minHeight)
25914                 )
25915         ){
25916             return false;
25917         }
25918         
25919         if(
25920                 this.isDocument &&
25921                 (this.rotate == 90 || this.rotate == 270) && 
25922                 (
25923                     width > this.imageEl.OriginWidth || 
25924                     height > this.imageEl.OriginHeight ||
25925                     (width < this.minHeight && height < this.minWidth)
25926                 )
25927         ){
25928             return false;
25929         }
25930         
25931         if(
25932                 !this.isDocument &&
25933                 (this.rotate == 0 || this.rotate == 180) && 
25934                 (
25935                     width < this.minWidth || 
25936                     width > this.imageEl.OriginWidth || 
25937                     height < this.minHeight || 
25938                     height > this.imageEl.OriginHeight
25939                 )
25940         ){
25941             return false;
25942         }
25943         
25944         if(
25945                 !this.isDocument &&
25946                 (this.rotate == 90 || this.rotate == 270) && 
25947                 (
25948                     width < this.minHeight || 
25949                     width > this.imageEl.OriginWidth || 
25950                     height < this.minWidth || 
25951                     height > this.imageEl.OriginHeight
25952                 )
25953         ){
25954             return false;
25955         }
25956         
25957         return true;
25958         
25959     },
25960     
25961     onRotateLeft : function(e)
25962     {   
25963         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25964             
25965             var minScale = this.thumbEl.getWidth() / this.minWidth;
25966             
25967             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25968             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25969             
25970             this.startScale = this.scale;
25971             
25972             while (this.getScaleLevel() < minScale){
25973             
25974                 this.scale = this.scale + 1;
25975                 
25976                 if(!this.zoomable()){
25977                     break;
25978                 }
25979                 
25980                 if(
25981                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25982                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25983                 ){
25984                     continue;
25985                 }
25986                 
25987                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25988
25989                 this.draw();
25990                 
25991                 return;
25992             }
25993             
25994             this.scale = this.startScale;
25995             
25996             this.onRotateFail();
25997             
25998             return false;
25999         }
26000         
26001         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26002
26003         if(this.isDocument){
26004             this.setThumbBoxSize();
26005             this.setThumbBoxPosition();
26006             this.setCanvasPosition();
26007         }
26008         
26009         this.draw();
26010         
26011         this.fireEvent('rotate', this, 'left');
26012         
26013     },
26014     
26015     onRotateRight : function(e)
26016     {
26017         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26018             
26019             var minScale = this.thumbEl.getWidth() / this.minWidth;
26020         
26021             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26022             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26023             
26024             this.startScale = this.scale;
26025             
26026             while (this.getScaleLevel() < minScale){
26027             
26028                 this.scale = this.scale + 1;
26029                 
26030                 if(!this.zoomable()){
26031                     break;
26032                 }
26033                 
26034                 if(
26035                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26036                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26037                 ){
26038                     continue;
26039                 }
26040                 
26041                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26042
26043                 this.draw();
26044                 
26045                 return;
26046             }
26047             
26048             this.scale = this.startScale;
26049             
26050             this.onRotateFail();
26051             
26052             return false;
26053         }
26054         
26055         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26056
26057         if(this.isDocument){
26058             this.setThumbBoxSize();
26059             this.setThumbBoxPosition();
26060             this.setCanvasPosition();
26061         }
26062         
26063         this.draw();
26064         
26065         this.fireEvent('rotate', this, 'right');
26066     },
26067     
26068     onRotateFail : function()
26069     {
26070         this.errorEl.show(true);
26071         
26072         var _this = this;
26073         
26074         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26075     },
26076     
26077     draw : function()
26078     {
26079         this.previewEl.dom.innerHTML = '';
26080         
26081         var canvasEl = document.createElement("canvas");
26082         
26083         var contextEl = canvasEl.getContext("2d");
26084         
26085         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26086         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26087         var center = this.imageEl.OriginWidth / 2;
26088         
26089         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26090             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26091             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26092             center = this.imageEl.OriginHeight / 2;
26093         }
26094         
26095         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26096         
26097         contextEl.translate(center, center);
26098         contextEl.rotate(this.rotate * Math.PI / 180);
26099
26100         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26101         
26102         this.canvasEl = document.createElement("canvas");
26103         
26104         this.contextEl = this.canvasEl.getContext("2d");
26105         
26106         switch (this.rotate) {
26107             case 0 :
26108                 
26109                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26110                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26111                 
26112                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26113                 
26114                 break;
26115             case 90 : 
26116                 
26117                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26118                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26119                 
26120                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26121                     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);
26122                     break;
26123                 }
26124                 
26125                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26126                 
26127                 break;
26128             case 180 :
26129                 
26130                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26131                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26132                 
26133                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26134                     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);
26135                     break;
26136                 }
26137                 
26138                 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);
26139                 
26140                 break;
26141             case 270 :
26142                 
26143                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26144                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26145         
26146                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26147                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26148                     break;
26149                 }
26150                 
26151                 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);
26152                 
26153                 break;
26154             default : 
26155                 break;
26156         }
26157         
26158         this.previewEl.appendChild(this.canvasEl);
26159         
26160         this.setCanvasPosition();
26161     },
26162     
26163     crop : function()
26164     {
26165         if(!this.canvasLoaded){
26166             return;
26167         }
26168         
26169         var imageCanvas = document.createElement("canvas");
26170         
26171         var imageContext = imageCanvas.getContext("2d");
26172         
26173         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26174         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26175         
26176         var center = imageCanvas.width / 2;
26177         
26178         imageContext.translate(center, center);
26179         
26180         imageContext.rotate(this.rotate * Math.PI / 180);
26181         
26182         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26183         
26184         var canvas = document.createElement("canvas");
26185         
26186         var context = canvas.getContext("2d");
26187                 
26188         canvas.width = this.minWidth;
26189         canvas.height = this.minHeight;
26190
26191         switch (this.rotate) {
26192             case 0 :
26193                 
26194                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26195                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26196                 
26197                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26198                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26199                 
26200                 var targetWidth = this.minWidth - 2 * x;
26201                 var targetHeight = this.minHeight - 2 * y;
26202                 
26203                 var scale = 1;
26204                 
26205                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26206                     scale = targetWidth / width;
26207                 }
26208                 
26209                 if(x > 0 && y == 0){
26210                     scale = targetHeight / height;
26211                 }
26212                 
26213                 if(x > 0 && y > 0){
26214                     scale = targetWidth / width;
26215                     
26216                     if(width < height){
26217                         scale = targetHeight / height;
26218                     }
26219                 }
26220                 
26221                 context.scale(scale, scale);
26222                 
26223                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26224                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26225
26226                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26227                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26228
26229                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26230                 
26231                 break;
26232             case 90 : 
26233                 
26234                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26235                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26236                 
26237                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26238                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26239                 
26240                 var targetWidth = this.minWidth - 2 * x;
26241                 var targetHeight = this.minHeight - 2 * y;
26242                 
26243                 var scale = 1;
26244                 
26245                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26246                     scale = targetWidth / width;
26247                 }
26248                 
26249                 if(x > 0 && y == 0){
26250                     scale = targetHeight / height;
26251                 }
26252                 
26253                 if(x > 0 && y > 0){
26254                     scale = targetWidth / width;
26255                     
26256                     if(width < height){
26257                         scale = targetHeight / height;
26258                     }
26259                 }
26260                 
26261                 context.scale(scale, scale);
26262                 
26263                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26264                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26265
26266                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26267                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26268                 
26269                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26270                 
26271                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26272                 
26273                 break;
26274             case 180 :
26275                 
26276                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26277                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26278                 
26279                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26280                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26281                 
26282                 var targetWidth = this.minWidth - 2 * x;
26283                 var targetHeight = this.minHeight - 2 * y;
26284                 
26285                 var scale = 1;
26286                 
26287                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26288                     scale = targetWidth / width;
26289                 }
26290                 
26291                 if(x > 0 && y == 0){
26292                     scale = targetHeight / height;
26293                 }
26294                 
26295                 if(x > 0 && y > 0){
26296                     scale = targetWidth / width;
26297                     
26298                     if(width < height){
26299                         scale = targetHeight / height;
26300                     }
26301                 }
26302                 
26303                 context.scale(scale, scale);
26304                 
26305                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26306                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26307
26308                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26309                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26310
26311                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26312                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26313                 
26314                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26315                 
26316                 break;
26317             case 270 :
26318                 
26319                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26320                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26321                 
26322                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26323                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26324                 
26325                 var targetWidth = this.minWidth - 2 * x;
26326                 var targetHeight = this.minHeight - 2 * y;
26327                 
26328                 var scale = 1;
26329                 
26330                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26331                     scale = targetWidth / width;
26332                 }
26333                 
26334                 if(x > 0 && y == 0){
26335                     scale = targetHeight / height;
26336                 }
26337                 
26338                 if(x > 0 && y > 0){
26339                     scale = targetWidth / width;
26340                     
26341                     if(width < height){
26342                         scale = targetHeight / height;
26343                     }
26344                 }
26345                 
26346                 context.scale(scale, scale);
26347                 
26348                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26349                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26350
26351                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26352                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26353                 
26354                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26355                 
26356                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26357                 
26358                 break;
26359             default : 
26360                 break;
26361         }
26362         
26363         this.cropData = canvas.toDataURL(this.cropType);
26364         
26365         if(this.fireEvent('crop', this, this.cropData) !== false){
26366             this.process(this.file, this.cropData);
26367         }
26368         
26369         return;
26370         
26371     },
26372     
26373     setThumbBoxSize : function()
26374     {
26375         var width, height;
26376         
26377         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26378             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26379             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26380             
26381             this.minWidth = width;
26382             this.minHeight = height;
26383             
26384             if(this.rotate == 90 || this.rotate == 270){
26385                 this.minWidth = height;
26386                 this.minHeight = width;
26387             }
26388         }
26389         
26390         height = 300;
26391         width = Math.ceil(this.minWidth * height / this.minHeight);
26392         
26393         if(this.minWidth > this.minHeight){
26394             width = 300;
26395             height = Math.ceil(this.minHeight * width / this.minWidth);
26396         }
26397         
26398         this.thumbEl.setStyle({
26399             width : width + 'px',
26400             height : height + 'px'
26401         });
26402
26403         return;
26404             
26405     },
26406     
26407     setThumbBoxPosition : function()
26408     {
26409         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26410         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26411         
26412         this.thumbEl.setLeft(x);
26413         this.thumbEl.setTop(y);
26414         
26415     },
26416     
26417     baseRotateLevel : function()
26418     {
26419         this.baseRotate = 1;
26420         
26421         if(
26422                 typeof(this.exif) != 'undefined' &&
26423                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26424                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26425         ){
26426             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26427         }
26428         
26429         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26430         
26431     },
26432     
26433     baseScaleLevel : function()
26434     {
26435         var width, height;
26436         
26437         if(this.isDocument){
26438             
26439             if(this.baseRotate == 6 || this.baseRotate == 8){
26440             
26441                 height = this.thumbEl.getHeight();
26442                 this.baseScale = height / this.imageEl.OriginWidth;
26443
26444                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26445                     width = this.thumbEl.getWidth();
26446                     this.baseScale = width / this.imageEl.OriginHeight;
26447                 }
26448
26449                 return;
26450             }
26451
26452             height = this.thumbEl.getHeight();
26453             this.baseScale = height / this.imageEl.OriginHeight;
26454
26455             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26456                 width = this.thumbEl.getWidth();
26457                 this.baseScale = width / this.imageEl.OriginWidth;
26458             }
26459
26460             return;
26461         }
26462         
26463         if(this.baseRotate == 6 || this.baseRotate == 8){
26464             
26465             width = this.thumbEl.getHeight();
26466             this.baseScale = width / this.imageEl.OriginHeight;
26467             
26468             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26469                 height = this.thumbEl.getWidth();
26470                 this.baseScale = height / this.imageEl.OriginHeight;
26471             }
26472             
26473             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26474                 height = this.thumbEl.getWidth();
26475                 this.baseScale = height / this.imageEl.OriginHeight;
26476                 
26477                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26478                     width = this.thumbEl.getHeight();
26479                     this.baseScale = width / this.imageEl.OriginWidth;
26480                 }
26481             }
26482             
26483             return;
26484         }
26485         
26486         width = this.thumbEl.getWidth();
26487         this.baseScale = width / this.imageEl.OriginWidth;
26488         
26489         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26490             height = this.thumbEl.getHeight();
26491             this.baseScale = height / this.imageEl.OriginHeight;
26492         }
26493         
26494         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26495             
26496             height = this.thumbEl.getHeight();
26497             this.baseScale = height / this.imageEl.OriginHeight;
26498             
26499             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26500                 width = this.thumbEl.getWidth();
26501                 this.baseScale = width / this.imageEl.OriginWidth;
26502             }
26503             
26504         }
26505         
26506         return;
26507     },
26508     
26509     getScaleLevel : function()
26510     {
26511         return this.baseScale * Math.pow(1.1, this.scale);
26512     },
26513     
26514     onTouchStart : function(e)
26515     {
26516         if(!this.canvasLoaded){
26517             this.beforeSelectFile(e);
26518             return;
26519         }
26520         
26521         var touches = e.browserEvent.touches;
26522         
26523         if(!touches){
26524             return;
26525         }
26526         
26527         if(touches.length == 1){
26528             this.onMouseDown(e);
26529             return;
26530         }
26531         
26532         if(touches.length != 2){
26533             return;
26534         }
26535         
26536         var coords = [];
26537         
26538         for(var i = 0, finger; finger = touches[i]; i++){
26539             coords.push(finger.pageX, finger.pageY);
26540         }
26541         
26542         var x = Math.pow(coords[0] - coords[2], 2);
26543         var y = Math.pow(coords[1] - coords[3], 2);
26544         
26545         this.startDistance = Math.sqrt(x + y);
26546         
26547         this.startScale = this.scale;
26548         
26549         this.pinching = true;
26550         this.dragable = false;
26551         
26552     },
26553     
26554     onTouchMove : function(e)
26555     {
26556         if(!this.pinching && !this.dragable){
26557             return;
26558         }
26559         
26560         var touches = e.browserEvent.touches;
26561         
26562         if(!touches){
26563             return;
26564         }
26565         
26566         if(this.dragable){
26567             this.onMouseMove(e);
26568             return;
26569         }
26570         
26571         var coords = [];
26572         
26573         for(var i = 0, finger; finger = touches[i]; i++){
26574             coords.push(finger.pageX, finger.pageY);
26575         }
26576         
26577         var x = Math.pow(coords[0] - coords[2], 2);
26578         var y = Math.pow(coords[1] - coords[3], 2);
26579         
26580         this.endDistance = Math.sqrt(x + y);
26581         
26582         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26583         
26584         if(!this.zoomable()){
26585             this.scale = this.startScale;
26586             return;
26587         }
26588         
26589         this.draw();
26590         
26591     },
26592     
26593     onTouchEnd : function(e)
26594     {
26595         this.pinching = false;
26596         this.dragable = false;
26597         
26598     },
26599     
26600     process : function(file, crop)
26601     {
26602         if(this.loadMask){
26603             this.maskEl.mask(this.loadingText);
26604         }
26605         
26606         this.xhr = new XMLHttpRequest();
26607         
26608         file.xhr = this.xhr;
26609
26610         this.xhr.open(this.method, this.url, true);
26611         
26612         var headers = {
26613             "Accept": "application/json",
26614             "Cache-Control": "no-cache",
26615             "X-Requested-With": "XMLHttpRequest"
26616         };
26617         
26618         for (var headerName in headers) {
26619             var headerValue = headers[headerName];
26620             if (headerValue) {
26621                 this.xhr.setRequestHeader(headerName, headerValue);
26622             }
26623         }
26624         
26625         var _this = this;
26626         
26627         this.xhr.onload = function()
26628         {
26629             _this.xhrOnLoad(_this.xhr);
26630         }
26631         
26632         this.xhr.onerror = function()
26633         {
26634             _this.xhrOnError(_this.xhr);
26635         }
26636         
26637         var formData = new FormData();
26638
26639         formData.append('returnHTML', 'NO');
26640         
26641         if(crop){
26642             formData.append('crop', crop);
26643         }
26644         
26645         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26646             formData.append(this.paramName, file, file.name);
26647         }
26648         
26649         if(typeof(file.filename) != 'undefined'){
26650             formData.append('filename', file.filename);
26651         }
26652         
26653         if(typeof(file.mimetype) != 'undefined'){
26654             formData.append('mimetype', file.mimetype);
26655         }
26656         
26657         if(this.fireEvent('arrange', this, formData) != false){
26658             this.xhr.send(formData);
26659         };
26660     },
26661     
26662     xhrOnLoad : function(xhr)
26663     {
26664         if(this.loadMask){
26665             this.maskEl.unmask();
26666         }
26667         
26668         if (xhr.readyState !== 4) {
26669             this.fireEvent('exception', this, xhr);
26670             return;
26671         }
26672
26673         var response = Roo.decode(xhr.responseText);
26674         
26675         if(!response.success){
26676             this.fireEvent('exception', this, xhr);
26677             return;
26678         }
26679         
26680         var response = Roo.decode(xhr.responseText);
26681         
26682         this.fireEvent('upload', this, response);
26683         
26684     },
26685     
26686     xhrOnError : function()
26687     {
26688         if(this.loadMask){
26689             this.maskEl.unmask();
26690         }
26691         
26692         Roo.log('xhr on error');
26693         
26694         var response = Roo.decode(xhr.responseText);
26695           
26696         Roo.log(response);
26697         
26698     },
26699     
26700     prepare : function(file)
26701     {   
26702         if(this.loadMask){
26703             this.maskEl.mask(this.loadingText);
26704         }
26705         
26706         this.file = false;
26707         this.exif = {};
26708         
26709         if(typeof(file) === 'string'){
26710             this.loadCanvas(file);
26711             return;
26712         }
26713         
26714         if(!file || !this.urlAPI){
26715             return;
26716         }
26717         
26718         this.file = file;
26719         this.cropType = file.type;
26720         
26721         var _this = this;
26722         
26723         if(this.fireEvent('prepare', this, this.file) != false){
26724             
26725             var reader = new FileReader();
26726             
26727             reader.onload = function (e) {
26728                 if (e.target.error) {
26729                     Roo.log(e.target.error);
26730                     return;
26731                 }
26732                 
26733                 var buffer = e.target.result,
26734                     dataView = new DataView(buffer),
26735                     offset = 2,
26736                     maxOffset = dataView.byteLength - 4,
26737                     markerBytes,
26738                     markerLength;
26739                 
26740                 if (dataView.getUint16(0) === 0xffd8) {
26741                     while (offset < maxOffset) {
26742                         markerBytes = dataView.getUint16(offset);
26743                         
26744                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26745                             markerLength = dataView.getUint16(offset + 2) + 2;
26746                             if (offset + markerLength > dataView.byteLength) {
26747                                 Roo.log('Invalid meta data: Invalid segment size.');
26748                                 break;
26749                             }
26750                             
26751                             if(markerBytes == 0xffe1){
26752                                 _this.parseExifData(
26753                                     dataView,
26754                                     offset,
26755                                     markerLength
26756                                 );
26757                             }
26758                             
26759                             offset += markerLength;
26760                             
26761                             continue;
26762                         }
26763                         
26764                         break;
26765                     }
26766                     
26767                 }
26768                 
26769                 var url = _this.urlAPI.createObjectURL(_this.file);
26770                 
26771                 _this.loadCanvas(url);
26772                 
26773                 return;
26774             }
26775             
26776             reader.readAsArrayBuffer(this.file);
26777             
26778         }
26779         
26780     },
26781     
26782     parseExifData : function(dataView, offset, length)
26783     {
26784         var tiffOffset = offset + 10,
26785             littleEndian,
26786             dirOffset;
26787     
26788         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26789             // No Exif data, might be XMP data instead
26790             return;
26791         }
26792         
26793         // Check for the ASCII code for "Exif" (0x45786966):
26794         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26795             // No Exif data, might be XMP data instead
26796             return;
26797         }
26798         if (tiffOffset + 8 > dataView.byteLength) {
26799             Roo.log('Invalid Exif data: Invalid segment size.');
26800             return;
26801         }
26802         // Check for the two null bytes:
26803         if (dataView.getUint16(offset + 8) !== 0x0000) {
26804             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26805             return;
26806         }
26807         // Check the byte alignment:
26808         switch (dataView.getUint16(tiffOffset)) {
26809         case 0x4949:
26810             littleEndian = true;
26811             break;
26812         case 0x4D4D:
26813             littleEndian = false;
26814             break;
26815         default:
26816             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26817             return;
26818         }
26819         // Check for the TIFF tag marker (0x002A):
26820         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26821             Roo.log('Invalid Exif data: Missing TIFF marker.');
26822             return;
26823         }
26824         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26825         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26826         
26827         this.parseExifTags(
26828             dataView,
26829             tiffOffset,
26830             tiffOffset + dirOffset,
26831             littleEndian
26832         );
26833     },
26834     
26835     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26836     {
26837         var tagsNumber,
26838             dirEndOffset,
26839             i;
26840         if (dirOffset + 6 > dataView.byteLength) {
26841             Roo.log('Invalid Exif data: Invalid directory offset.');
26842             return;
26843         }
26844         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26845         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26846         if (dirEndOffset + 4 > dataView.byteLength) {
26847             Roo.log('Invalid Exif data: Invalid directory size.');
26848             return;
26849         }
26850         for (i = 0; i < tagsNumber; i += 1) {
26851             this.parseExifTag(
26852                 dataView,
26853                 tiffOffset,
26854                 dirOffset + 2 + 12 * i, // tag offset
26855                 littleEndian
26856             );
26857         }
26858         // Return the offset to the next directory:
26859         return dataView.getUint32(dirEndOffset, littleEndian);
26860     },
26861     
26862     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26863     {
26864         var tag = dataView.getUint16(offset, littleEndian);
26865         
26866         this.exif[tag] = this.getExifValue(
26867             dataView,
26868             tiffOffset,
26869             offset,
26870             dataView.getUint16(offset + 2, littleEndian), // tag type
26871             dataView.getUint32(offset + 4, littleEndian), // tag length
26872             littleEndian
26873         );
26874     },
26875     
26876     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26877     {
26878         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26879             tagSize,
26880             dataOffset,
26881             values,
26882             i,
26883             str,
26884             c;
26885     
26886         if (!tagType) {
26887             Roo.log('Invalid Exif data: Invalid tag type.');
26888             return;
26889         }
26890         
26891         tagSize = tagType.size * length;
26892         // Determine if the value is contained in the dataOffset bytes,
26893         // or if the value at the dataOffset is a pointer to the actual data:
26894         dataOffset = tagSize > 4 ?
26895                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26896         if (dataOffset + tagSize > dataView.byteLength) {
26897             Roo.log('Invalid Exif data: Invalid data offset.');
26898             return;
26899         }
26900         if (length === 1) {
26901             return tagType.getValue(dataView, dataOffset, littleEndian);
26902         }
26903         values = [];
26904         for (i = 0; i < length; i += 1) {
26905             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26906         }
26907         
26908         if (tagType.ascii) {
26909             str = '';
26910             // Concatenate the chars:
26911             for (i = 0; i < values.length; i += 1) {
26912                 c = values[i];
26913                 // Ignore the terminating NULL byte(s):
26914                 if (c === '\u0000') {
26915                     break;
26916                 }
26917                 str += c;
26918             }
26919             return str;
26920         }
26921         return values;
26922     }
26923     
26924 });
26925
26926 Roo.apply(Roo.bootstrap.UploadCropbox, {
26927     tags : {
26928         'Orientation': 0x0112
26929     },
26930     
26931     Orientation: {
26932             1: 0, //'top-left',
26933 //            2: 'top-right',
26934             3: 180, //'bottom-right',
26935 //            4: 'bottom-left',
26936 //            5: 'left-top',
26937             6: 90, //'right-top',
26938 //            7: 'right-bottom',
26939             8: 270 //'left-bottom'
26940     },
26941     
26942     exifTagTypes : {
26943         // byte, 8-bit unsigned int:
26944         1: {
26945             getValue: function (dataView, dataOffset) {
26946                 return dataView.getUint8(dataOffset);
26947             },
26948             size: 1
26949         },
26950         // ascii, 8-bit byte:
26951         2: {
26952             getValue: function (dataView, dataOffset) {
26953                 return String.fromCharCode(dataView.getUint8(dataOffset));
26954             },
26955             size: 1,
26956             ascii: true
26957         },
26958         // short, 16 bit int:
26959         3: {
26960             getValue: function (dataView, dataOffset, littleEndian) {
26961                 return dataView.getUint16(dataOffset, littleEndian);
26962             },
26963             size: 2
26964         },
26965         // long, 32 bit int:
26966         4: {
26967             getValue: function (dataView, dataOffset, littleEndian) {
26968                 return dataView.getUint32(dataOffset, littleEndian);
26969             },
26970             size: 4
26971         },
26972         // rational = two long values, first is numerator, second is denominator:
26973         5: {
26974             getValue: function (dataView, dataOffset, littleEndian) {
26975                 return dataView.getUint32(dataOffset, littleEndian) /
26976                     dataView.getUint32(dataOffset + 4, littleEndian);
26977             },
26978             size: 8
26979         },
26980         // slong, 32 bit signed int:
26981         9: {
26982             getValue: function (dataView, dataOffset, littleEndian) {
26983                 return dataView.getInt32(dataOffset, littleEndian);
26984             },
26985             size: 4
26986         },
26987         // srational, two slongs, first is numerator, second is denominator:
26988         10: {
26989             getValue: function (dataView, dataOffset, littleEndian) {
26990                 return dataView.getInt32(dataOffset, littleEndian) /
26991                     dataView.getInt32(dataOffset + 4, littleEndian);
26992             },
26993             size: 8
26994         }
26995     },
26996     
26997     footer : {
26998         STANDARD : [
26999             {
27000                 tag : 'div',
27001                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27002                 action : 'rotate-left',
27003                 cn : [
27004                     {
27005                         tag : 'button',
27006                         cls : 'btn btn-default',
27007                         html : '<i class="fa fa-undo"></i>'
27008                     }
27009                 ]
27010             },
27011             {
27012                 tag : 'div',
27013                 cls : 'btn-group roo-upload-cropbox-picture',
27014                 action : 'picture',
27015                 cn : [
27016                     {
27017                         tag : 'button',
27018                         cls : 'btn btn-default',
27019                         html : '<i class="fa fa-picture-o"></i>'
27020                     }
27021                 ]
27022             },
27023             {
27024                 tag : 'div',
27025                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27026                 action : 'rotate-right',
27027                 cn : [
27028                     {
27029                         tag : 'button',
27030                         cls : 'btn btn-default',
27031                         html : '<i class="fa fa-repeat"></i>'
27032                     }
27033                 ]
27034             }
27035         ],
27036         DOCUMENT : [
27037             {
27038                 tag : 'div',
27039                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27040                 action : 'rotate-left',
27041                 cn : [
27042                     {
27043                         tag : 'button',
27044                         cls : 'btn btn-default',
27045                         html : '<i class="fa fa-undo"></i>'
27046                     }
27047                 ]
27048             },
27049             {
27050                 tag : 'div',
27051                 cls : 'btn-group roo-upload-cropbox-download',
27052                 action : 'download',
27053                 cn : [
27054                     {
27055                         tag : 'button',
27056                         cls : 'btn btn-default',
27057                         html : '<i class="fa fa-download"></i>'
27058                     }
27059                 ]
27060             },
27061             {
27062                 tag : 'div',
27063                 cls : 'btn-group roo-upload-cropbox-crop',
27064                 action : 'crop',
27065                 cn : [
27066                     {
27067                         tag : 'button',
27068                         cls : 'btn btn-default',
27069                         html : '<i class="fa fa-crop"></i>'
27070                     }
27071                 ]
27072             },
27073             {
27074                 tag : 'div',
27075                 cls : 'btn-group roo-upload-cropbox-trash',
27076                 action : 'trash',
27077                 cn : [
27078                     {
27079                         tag : 'button',
27080                         cls : 'btn btn-default',
27081                         html : '<i class="fa fa-trash"></i>'
27082                     }
27083                 ]
27084             },
27085             {
27086                 tag : 'div',
27087                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27088                 action : 'rotate-right',
27089                 cn : [
27090                     {
27091                         tag : 'button',
27092                         cls : 'btn btn-default',
27093                         html : '<i class="fa fa-repeat"></i>'
27094                     }
27095                 ]
27096             }
27097         ],
27098         ROTATOR : [
27099             {
27100                 tag : 'div',
27101                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27102                 action : 'rotate-left',
27103                 cn : [
27104                     {
27105                         tag : 'button',
27106                         cls : 'btn btn-default',
27107                         html : '<i class="fa fa-undo"></i>'
27108                     }
27109                 ]
27110             },
27111             {
27112                 tag : 'div',
27113                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27114                 action : 'rotate-right',
27115                 cn : [
27116                     {
27117                         tag : 'button',
27118                         cls : 'btn btn-default',
27119                         html : '<i class="fa fa-repeat"></i>'
27120                     }
27121                 ]
27122             }
27123         ]
27124     }
27125 });
27126
27127 /*
27128 * Licence: LGPL
27129 */
27130
27131 /**
27132  * @class Roo.bootstrap.DocumentManager
27133  * @extends Roo.bootstrap.Component
27134  * Bootstrap DocumentManager class
27135  * @cfg {String} paramName default 'imageUpload'
27136  * @cfg {String} method default POST
27137  * @cfg {String} url action url
27138  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27139  * @cfg {Boolean} multiple multiple upload default true
27140  * @cfg {Number} thumbSize default 300
27141  * @cfg {String} fieldLabel
27142  * @cfg {Number} labelWidth default 4
27143  * @cfg {String} labelAlign (left|top) default left
27144  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27145  * 
27146  * @constructor
27147  * Create a new DocumentManager
27148  * @param {Object} config The config object
27149  */
27150
27151 Roo.bootstrap.DocumentManager = function(config){
27152     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27153     
27154     this.addEvents({
27155         /**
27156          * @event initial
27157          * Fire when initial the DocumentManager
27158          * @param {Roo.bootstrap.DocumentManager} this
27159          */
27160         "initial" : true,
27161         /**
27162          * @event inspect
27163          * inspect selected file
27164          * @param {Roo.bootstrap.DocumentManager} this
27165          * @param {File} file
27166          */
27167         "inspect" : true,
27168         /**
27169          * @event exception
27170          * Fire when xhr load exception
27171          * @param {Roo.bootstrap.DocumentManager} this
27172          * @param {XMLHttpRequest} xhr
27173          */
27174         "exception" : true,
27175         /**
27176          * @event prepare
27177          * prepare the form data
27178          * @param {Roo.bootstrap.DocumentManager} this
27179          * @param {Object} formData
27180          */
27181         "prepare" : true,
27182         /**
27183          * @event remove
27184          * Fire when remove the file
27185          * @param {Roo.bootstrap.DocumentManager} this
27186          * @param {Object} file
27187          */
27188         "remove" : true,
27189         /**
27190          * @event refresh
27191          * Fire after refresh the file
27192          * @param {Roo.bootstrap.DocumentManager} this
27193          */
27194         "refresh" : true,
27195         /**
27196          * @event click
27197          * Fire after click the image
27198          * @param {Roo.bootstrap.DocumentManager} this
27199          * @param {Object} file
27200          */
27201         "click" : true,
27202         /**
27203          * @event edit
27204          * Fire when upload a image and editable set to true
27205          * @param {Roo.bootstrap.DocumentManager} this
27206          * @param {Object} file
27207          */
27208         "edit" : true,
27209         /**
27210          * @event beforeselectfile
27211          * Fire before select file
27212          * @param {Roo.bootstrap.DocumentManager} this
27213          */
27214         "beforeselectfile" : true,
27215         /**
27216          * @event process
27217          * Fire before process file
27218          * @param {Roo.bootstrap.DocumentManager} this
27219          * @param {Object} file
27220          */
27221         "process" : true
27222         
27223     });
27224 };
27225
27226 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27227     
27228     boxes : 0,
27229     inputName : '',
27230     thumbSize : 300,
27231     multiple : true,
27232     files : [],
27233     method : 'POST',
27234     url : '',
27235     paramName : 'imageUpload',
27236     fieldLabel : '',
27237     labelWidth : 4,
27238     labelAlign : 'left',
27239     editable : true,
27240     delegates : [],
27241     
27242     
27243     xhr : false, 
27244     
27245     getAutoCreate : function()
27246     {   
27247         var managerWidget = {
27248             tag : 'div',
27249             cls : 'roo-document-manager',
27250             cn : [
27251                 {
27252                     tag : 'input',
27253                     cls : 'roo-document-manager-selector',
27254                     type : 'file'
27255                 },
27256                 {
27257                     tag : 'div',
27258                     cls : 'roo-document-manager-uploader',
27259                     cn : [
27260                         {
27261                             tag : 'div',
27262                             cls : 'roo-document-manager-upload-btn',
27263                             html : '<i class="fa fa-plus"></i>'
27264                         }
27265                     ]
27266                     
27267                 }
27268             ]
27269         };
27270         
27271         var content = [
27272             {
27273                 tag : 'div',
27274                 cls : 'column col-md-12',
27275                 cn : managerWidget
27276             }
27277         ];
27278         
27279         if(this.fieldLabel.length){
27280             
27281             content = [
27282                 {
27283                     tag : 'div',
27284                     cls : 'column col-md-12',
27285                     html : this.fieldLabel
27286                 },
27287                 {
27288                     tag : 'div',
27289                     cls : 'column col-md-12',
27290                     cn : managerWidget
27291                 }
27292             ];
27293
27294             if(this.labelAlign == 'left'){
27295                 content = [
27296                     {
27297                         tag : 'div',
27298                         cls : 'column col-md-' + this.labelWidth,
27299                         html : this.fieldLabel
27300                     },
27301                     {
27302                         tag : 'div',
27303                         cls : 'column col-md-' + (12 - this.labelWidth),
27304                         cn : managerWidget
27305                     }
27306                 ];
27307                 
27308             }
27309         }
27310         
27311         var cfg = {
27312             tag : 'div',
27313             cls : 'row clearfix',
27314             cn : content
27315         };
27316         
27317         return cfg;
27318         
27319     },
27320     
27321     initEvents : function()
27322     {
27323         this.managerEl = this.el.select('.roo-document-manager', true).first();
27324         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27325         
27326         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27327         this.selectorEl.hide();
27328         
27329         if(this.multiple){
27330             this.selectorEl.attr('multiple', 'multiple');
27331         }
27332         
27333         this.selectorEl.on('change', this.onFileSelected, this);
27334         
27335         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27336         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27337         
27338         this.uploader.on('click', this.onUploaderClick, this);
27339         
27340         this.renderProgressDialog();
27341         
27342         var _this = this;
27343         
27344         window.addEventListener("resize", function() { _this.refresh(); } );
27345         
27346         this.fireEvent('initial', this);
27347     },
27348     
27349     renderProgressDialog : function()
27350     {
27351         var _this = this;
27352         
27353         this.progressDialog = new Roo.bootstrap.Modal({
27354             cls : 'roo-document-manager-progress-dialog',
27355             allow_close : false,
27356             title : '',
27357             buttons : [
27358                 {
27359                     name  :'cancel',
27360                     weight : 'danger',
27361                     html : 'Cancel'
27362                 }
27363             ], 
27364             listeners : { 
27365                 btnclick : function() {
27366                     _this.uploadCancel();
27367                     this.hide();
27368                 }
27369             }
27370         });
27371          
27372         this.progressDialog.render(Roo.get(document.body));
27373          
27374         this.progress = new Roo.bootstrap.Progress({
27375             cls : 'roo-document-manager-progress',
27376             active : true,
27377             striped : true
27378         });
27379         
27380         this.progress.render(this.progressDialog.getChildContainer());
27381         
27382         this.progressBar = new Roo.bootstrap.ProgressBar({
27383             cls : 'roo-document-manager-progress-bar',
27384             aria_valuenow : 0,
27385             aria_valuemin : 0,
27386             aria_valuemax : 12,
27387             panel : 'success'
27388         });
27389         
27390         this.progressBar.render(this.progress.getChildContainer());
27391     },
27392     
27393     onUploaderClick : function(e)
27394     {
27395         e.preventDefault();
27396      
27397         if(this.fireEvent('beforeselectfile', this) != false){
27398             this.selectorEl.dom.click();
27399         }
27400         
27401     },
27402     
27403     onFileSelected : function(e)
27404     {
27405         e.preventDefault();
27406         
27407         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27408             return;
27409         }
27410         
27411         Roo.each(this.selectorEl.dom.files, function(file){
27412             if(this.fireEvent('inspect', this, file) != false){
27413                 this.files.push(file);
27414             }
27415         }, this);
27416         
27417         this.queue();
27418         
27419     },
27420     
27421     queue : function()
27422     {
27423         this.selectorEl.dom.value = '';
27424         
27425         if(!this.files.length){
27426             return;
27427         }
27428         
27429         if(this.boxes > 0 && this.files.length > this.boxes){
27430             this.files = this.files.slice(0, this.boxes);
27431         }
27432         
27433         this.uploader.show();
27434         
27435         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27436             this.uploader.hide();
27437         }
27438         
27439         var _this = this;
27440         
27441         var files = [];
27442         
27443         var docs = [];
27444         
27445         Roo.each(this.files, function(file){
27446             
27447             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27448                 var f = this.renderPreview(file);
27449                 files.push(f);
27450                 return;
27451             }
27452             
27453             if(file.type.indexOf('image') != -1){
27454                 this.delegates.push(
27455                     (function(){
27456                         _this.process(file);
27457                     }).createDelegate(this)
27458                 );
27459         
27460                 return;
27461             }
27462             
27463             docs.push(
27464                 (function(){
27465                     _this.process(file);
27466                 }).createDelegate(this)
27467             );
27468             
27469         }, this);
27470         
27471         this.files = files;
27472         
27473         this.delegates = this.delegates.concat(docs);
27474         
27475         if(!this.delegates.length){
27476             this.refresh();
27477             return;
27478         }
27479         
27480         this.progressBar.aria_valuemax = this.delegates.length;
27481         
27482         this.arrange();
27483         
27484         return;
27485     },
27486     
27487     arrange : function()
27488     {
27489         if(!this.delegates.length){
27490             this.progressDialog.hide();
27491             this.refresh();
27492             return;
27493         }
27494         
27495         var delegate = this.delegates.shift();
27496         
27497         this.progressDialog.show();
27498         
27499         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27500         
27501         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27502         
27503         delegate();
27504     },
27505     
27506     refresh : function()
27507     {
27508         this.uploader.show();
27509         
27510         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27511             this.uploader.hide();
27512         }
27513         
27514         Roo.isTouch ? this.closable(false) : this.closable(true);
27515         
27516         this.fireEvent('refresh', this);
27517     },
27518     
27519     onRemove : function(e, el, o)
27520     {
27521         e.preventDefault();
27522         
27523         this.fireEvent('remove', this, o);
27524         
27525     },
27526     
27527     remove : function(o)
27528     {
27529         var files = [];
27530         
27531         Roo.each(this.files, function(file){
27532             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27533                 files.push(file);
27534                 return;
27535             }
27536
27537             o.target.remove();
27538
27539         }, this);
27540         
27541         this.files = files;
27542         
27543         this.refresh();
27544     },
27545     
27546     clear : function()
27547     {
27548         Roo.each(this.files, function(file){
27549             if(!file.target){
27550                 return;
27551             }
27552             
27553             file.target.remove();
27554
27555         }, this);
27556         
27557         this.files = [];
27558         
27559         this.refresh();
27560     },
27561     
27562     onClick : function(e, el, o)
27563     {
27564         e.preventDefault();
27565         
27566         this.fireEvent('click', this, o);
27567         
27568     },
27569     
27570     closable : function(closable)
27571     {
27572         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27573             
27574             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27575             
27576             if(closable){
27577                 el.show();
27578                 return;
27579             }
27580             
27581             el.hide();
27582             
27583         }, this);
27584     },
27585     
27586     xhrOnLoad : function(xhr)
27587     {
27588         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27589             el.remove();
27590         }, this);
27591         
27592         if (xhr.readyState !== 4) {
27593             this.arrange();
27594             this.fireEvent('exception', this, xhr);
27595             return;
27596         }
27597
27598         var response = Roo.decode(xhr.responseText);
27599         
27600         if(!response.success){
27601             this.arrange();
27602             this.fireEvent('exception', this, xhr);
27603             return;
27604         }
27605         
27606         var file = this.renderPreview(response.data);
27607         
27608         this.files.push(file);
27609         
27610         this.arrange();
27611         
27612     },
27613     
27614     xhrOnError : function(xhr)
27615     {
27616         Roo.log('xhr on error');
27617         
27618         var response = Roo.decode(xhr.responseText);
27619           
27620         Roo.log(response);
27621         
27622         this.arrange();
27623     },
27624     
27625     process : function(file)
27626     {
27627         if(this.fireEvent('process', this, file) !== false){
27628             if(this.editable && file.type.indexOf('image') != -1){
27629                 this.fireEvent('edit', this, file);
27630                 return;
27631             }
27632
27633             this.uploadStart(file, false);
27634
27635             return;
27636         }
27637         
27638     },
27639     
27640     uploadStart : function(file, crop)
27641     {
27642         this.xhr = new XMLHttpRequest();
27643         
27644         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27645             this.arrange();
27646             return;
27647         }
27648         
27649         file.xhr = this.xhr;
27650             
27651         this.managerEl.createChild({
27652             tag : 'div',
27653             cls : 'roo-document-manager-loading',
27654             cn : [
27655                 {
27656                     tag : 'div',
27657                     tooltip : file.name,
27658                     cls : 'roo-document-manager-thumb',
27659                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27660                 }
27661             ]
27662
27663         });
27664
27665         this.xhr.open(this.method, this.url, true);
27666         
27667         var headers = {
27668             "Accept": "application/json",
27669             "Cache-Control": "no-cache",
27670             "X-Requested-With": "XMLHttpRequest"
27671         };
27672         
27673         for (var headerName in headers) {
27674             var headerValue = headers[headerName];
27675             if (headerValue) {
27676                 this.xhr.setRequestHeader(headerName, headerValue);
27677             }
27678         }
27679         
27680         var _this = this;
27681         
27682         this.xhr.onload = function()
27683         {
27684             _this.xhrOnLoad(_this.xhr);
27685         }
27686         
27687         this.xhr.onerror = function()
27688         {
27689             _this.xhrOnError(_this.xhr);
27690         }
27691         
27692         var formData = new FormData();
27693
27694         formData.append('returnHTML', 'NO');
27695         
27696         if(crop){
27697             formData.append('crop', crop);
27698         }
27699         
27700         formData.append(this.paramName, file, file.name);
27701         
27702         if(this.fireEvent('prepare', this, formData) != false){
27703             this.xhr.send(formData);
27704         };
27705     },
27706     
27707     uploadCancel : function()
27708     {
27709         if (this.xhr) {
27710             this.xhr.abort();
27711         }
27712         
27713         
27714         this.delegates = [];
27715         
27716         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27717             el.remove();
27718         }, this);
27719         
27720         this.arrange();
27721     },
27722     
27723     renderPreview : function(file)
27724     {
27725         if(typeof(file.target) != 'undefined' && file.target){
27726             return file;
27727         }
27728         
27729         var previewEl = this.managerEl.createChild({
27730             tag : 'div',
27731             cls : 'roo-document-manager-preview',
27732             cn : [
27733                 {
27734                     tag : 'div',
27735                     tooltip : file.filename,
27736                     cls : 'roo-document-manager-thumb',
27737                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27738                 },
27739                 {
27740                     tag : 'button',
27741                     cls : 'close',
27742                     html : '<i class="fa fa-times-circle"></i>'
27743                 }
27744             ]
27745         });
27746
27747         var close = previewEl.select('button.close', true).first();
27748
27749         close.on('click', this.onRemove, this, file);
27750
27751         file.target = previewEl;
27752
27753         var image = previewEl.select('img', true).first();
27754         
27755         var _this = this;
27756         
27757         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27758         
27759         image.on('click', this.onClick, this, file);
27760         
27761         return file;
27762         
27763     },
27764     
27765     onPreviewLoad : function(file, image)
27766     {
27767         if(typeof(file.target) == 'undefined' || !file.target){
27768             return;
27769         }
27770         
27771         var width = image.dom.naturalWidth || image.dom.width;
27772         var height = image.dom.naturalHeight || image.dom.height;
27773         
27774         if(width > height){
27775             file.target.addClass('wide');
27776             return;
27777         }
27778         
27779         file.target.addClass('tall');
27780         return;
27781         
27782     },
27783     
27784     uploadFromSource : function(file, crop)
27785     {
27786         this.xhr = new XMLHttpRequest();
27787         
27788         this.managerEl.createChild({
27789             tag : 'div',
27790             cls : 'roo-document-manager-loading',
27791             cn : [
27792                 {
27793                     tag : 'div',
27794                     tooltip : file.name,
27795                     cls : 'roo-document-manager-thumb',
27796                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27797                 }
27798             ]
27799
27800         });
27801
27802         this.xhr.open(this.method, this.url, true);
27803         
27804         var headers = {
27805             "Accept": "application/json",
27806             "Cache-Control": "no-cache",
27807             "X-Requested-With": "XMLHttpRequest"
27808         };
27809         
27810         for (var headerName in headers) {
27811             var headerValue = headers[headerName];
27812             if (headerValue) {
27813                 this.xhr.setRequestHeader(headerName, headerValue);
27814             }
27815         }
27816         
27817         var _this = this;
27818         
27819         this.xhr.onload = function()
27820         {
27821             _this.xhrOnLoad(_this.xhr);
27822         }
27823         
27824         this.xhr.onerror = function()
27825         {
27826             _this.xhrOnError(_this.xhr);
27827         }
27828         
27829         var formData = new FormData();
27830
27831         formData.append('returnHTML', 'NO');
27832         
27833         formData.append('crop', crop);
27834         
27835         if(typeof(file.filename) != 'undefined'){
27836             formData.append('filename', file.filename);
27837         }
27838         
27839         if(typeof(file.mimetype) != 'undefined'){
27840             formData.append('mimetype', file.mimetype);
27841         }
27842         
27843         if(this.fireEvent('prepare', this, formData) != false){
27844             this.xhr.send(formData);
27845         };
27846     }
27847 });
27848
27849 /*
27850 * Licence: LGPL
27851 */
27852
27853 /**
27854  * @class Roo.bootstrap.DocumentViewer
27855  * @extends Roo.bootstrap.Component
27856  * Bootstrap DocumentViewer class
27857  * 
27858  * @constructor
27859  * Create a new DocumentViewer
27860  * @param {Object} config The config object
27861  */
27862
27863 Roo.bootstrap.DocumentViewer = function(config){
27864     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27865     
27866     this.addEvents({
27867         /**
27868          * @event initial
27869          * Fire after initEvent
27870          * @param {Roo.bootstrap.DocumentViewer} this
27871          */
27872         "initial" : true,
27873         /**
27874          * @event click
27875          * Fire after click
27876          * @param {Roo.bootstrap.DocumentViewer} this
27877          */
27878         "click" : true,
27879         /**
27880          * @event trash
27881          * Fire after trash button
27882          * @param {Roo.bootstrap.DocumentViewer} this
27883          */
27884         "trash" : true
27885         
27886     });
27887 };
27888
27889 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27890     
27891     getAutoCreate : function()
27892     {
27893         var cfg = {
27894             tag : 'div',
27895             cls : 'roo-document-viewer',
27896             cn : [
27897                 {
27898                     tag : 'div',
27899                     cls : 'roo-document-viewer-body',
27900                     cn : [
27901                         {
27902                             tag : 'div',
27903                             cls : 'roo-document-viewer-thumb',
27904                             cn : [
27905                                 {
27906                                     tag : 'img',
27907                                     cls : 'roo-document-viewer-image'
27908                                 }
27909                             ]
27910                         }
27911                     ]
27912                 },
27913                 {
27914                     tag : 'div',
27915                     cls : 'roo-document-viewer-footer',
27916                     cn : {
27917                         tag : 'div',
27918                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27919                         cn : [
27920                             {
27921                                 tag : 'div',
27922                                 cls : 'btn-group',
27923                                 cn : [
27924                                     {
27925                                         tag : 'button',
27926                                         cls : 'btn btn-default roo-document-viewer-trash',
27927                                         html : '<i class="fa fa-trash"></i>'
27928                                     }
27929                                 ]
27930                             }
27931                         ]
27932                     }
27933                 }
27934             ]
27935         };
27936         
27937         return cfg;
27938     },
27939     
27940     initEvents : function()
27941     {
27942         
27943         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27944         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27945         
27946         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27947         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27948         
27949         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27950         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27951         
27952         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27953         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27954         
27955         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27956         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27957         
27958         this.bodyEl.on('click', this.onClick, this);
27959         
27960         this.trashBtn.on('click', this.onTrash, this);
27961         
27962     },
27963     
27964     initial : function()
27965     {
27966 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27967         
27968         
27969         this.fireEvent('initial', this);
27970         
27971     },
27972     
27973     onClick : function(e)
27974     {
27975         e.preventDefault();
27976         
27977         this.fireEvent('click', this);
27978     },
27979     
27980     onTrash : function(e)
27981     {
27982         e.preventDefault();
27983         
27984         this.fireEvent('trash', this);
27985     }
27986     
27987 });
27988 /*
27989  * - LGPL
27990  *
27991  * nav progress bar
27992  * 
27993  */
27994
27995 /**
27996  * @class Roo.bootstrap.NavProgressBar
27997  * @extends Roo.bootstrap.Component
27998  * Bootstrap NavProgressBar class
27999  * 
28000  * @constructor
28001  * Create a new nav progress bar
28002  * @param {Object} config The config object
28003  */
28004
28005 Roo.bootstrap.NavProgressBar = function(config){
28006     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28007
28008     this.bullets = this.bullets || [];
28009    
28010 //    Roo.bootstrap.NavProgressBar.register(this);
28011      this.addEvents({
28012         /**
28013              * @event changed
28014              * Fires when the active item changes
28015              * @param {Roo.bootstrap.NavProgressBar} this
28016              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28017              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28018          */
28019         'changed': true
28020      });
28021     
28022 };
28023
28024 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28025     
28026     bullets : [],
28027     barItems : [],
28028     
28029     getAutoCreate : function()
28030     {
28031         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28032         
28033         cfg = {
28034             tag : 'div',
28035             cls : 'roo-navigation-bar-group',
28036             cn : [
28037                 {
28038                     tag : 'div',
28039                     cls : 'roo-navigation-top-bar'
28040                 },
28041                 {
28042                     tag : 'div',
28043                     cls : 'roo-navigation-bullets-bar',
28044                     cn : [
28045                         {
28046                             tag : 'ul',
28047                             cls : 'roo-navigation-bar'
28048                         }
28049                     ]
28050                 },
28051                 
28052                 {
28053                     tag : 'div',
28054                     cls : 'roo-navigation-bottom-bar'
28055                 }
28056             ]
28057             
28058         };
28059         
28060         return cfg;
28061         
28062     },
28063     
28064     initEvents: function() 
28065     {
28066         
28067     },
28068     
28069     onRender : function(ct, position) 
28070     {
28071         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28072         
28073         if(this.bullets.length){
28074             Roo.each(this.bullets, function(b){
28075                this.addItem(b);
28076             }, this);
28077         }
28078         
28079         this.format();
28080         
28081     },
28082     
28083     addItem : function(cfg)
28084     {
28085         var item = new Roo.bootstrap.NavProgressItem(cfg);
28086         
28087         item.parentId = this.id;
28088         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28089         
28090         if(cfg.html){
28091             var top = new Roo.bootstrap.Element({
28092                 tag : 'div',
28093                 cls : 'roo-navigation-bar-text'
28094             });
28095             
28096             var bottom = new Roo.bootstrap.Element({
28097                 tag : 'div',
28098                 cls : 'roo-navigation-bar-text'
28099             });
28100             
28101             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28102             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28103             
28104             var topText = new Roo.bootstrap.Element({
28105                 tag : 'span',
28106                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28107             });
28108             
28109             var bottomText = new Roo.bootstrap.Element({
28110                 tag : 'span',
28111                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28112             });
28113             
28114             topText.onRender(top.el, null);
28115             bottomText.onRender(bottom.el, null);
28116             
28117             item.topEl = top;
28118             item.bottomEl = bottom;
28119         }
28120         
28121         this.barItems.push(item);
28122         
28123         return item;
28124     },
28125     
28126     getActive : function()
28127     {
28128         var active = false;
28129         
28130         Roo.each(this.barItems, function(v){
28131             
28132             if (!v.isActive()) {
28133                 return;
28134             }
28135             
28136             active = v;
28137             return false;
28138             
28139         });
28140         
28141         return active;
28142     },
28143     
28144     setActiveItem : function(item)
28145     {
28146         var prev = false;
28147         
28148         Roo.each(this.barItems, function(v){
28149             if (v.rid == item.rid) {
28150                 return ;
28151             }
28152             
28153             if (v.isActive()) {
28154                 v.setActive(false);
28155                 prev = v;
28156             }
28157         });
28158
28159         item.setActive(true);
28160         
28161         this.fireEvent('changed', this, item, prev);
28162     },
28163     
28164     getBarItem: function(rid)
28165     {
28166         var ret = false;
28167         
28168         Roo.each(this.barItems, function(e) {
28169             if (e.rid != rid) {
28170                 return;
28171             }
28172             
28173             ret =  e;
28174             return false;
28175         });
28176         
28177         return ret;
28178     },
28179     
28180     indexOfItem : function(item)
28181     {
28182         var index = false;
28183         
28184         Roo.each(this.barItems, function(v, i){
28185             
28186             if (v.rid != item.rid) {
28187                 return;
28188             }
28189             
28190             index = i;
28191             return false
28192         });
28193         
28194         return index;
28195     },
28196     
28197     setActiveNext : function()
28198     {
28199         var i = this.indexOfItem(this.getActive());
28200         
28201         if (i > this.barItems.length) {
28202             return;
28203         }
28204         
28205         this.setActiveItem(this.barItems[i+1]);
28206     },
28207     
28208     setActivePrev : function()
28209     {
28210         var i = this.indexOfItem(this.getActive());
28211         
28212         if (i  < 1) {
28213             return;
28214         }
28215         
28216         this.setActiveItem(this.barItems[i-1]);
28217     },
28218     
28219     format : function()
28220     {
28221         if(!this.barItems.length){
28222             return;
28223         }
28224      
28225         var width = 100 / this.barItems.length;
28226         
28227         Roo.each(this.barItems, function(i){
28228             i.el.setStyle('width', width + '%');
28229             i.topEl.el.setStyle('width', width + '%');
28230             i.bottomEl.el.setStyle('width', width + '%');
28231         }, this);
28232         
28233     }
28234     
28235 });
28236 /*
28237  * - LGPL
28238  *
28239  * Nav Progress Item
28240  * 
28241  */
28242
28243 /**
28244  * @class Roo.bootstrap.NavProgressItem
28245  * @extends Roo.bootstrap.Component
28246  * Bootstrap NavProgressItem class
28247  * @cfg {String} rid the reference id
28248  * @cfg {Boolean} active (true|false) Is item active default false
28249  * @cfg {Boolean} disabled (true|false) Is item active default false
28250  * @cfg {String} html
28251  * @cfg {String} position (top|bottom) text position default bottom
28252  * @cfg {String} icon show icon instead of number
28253  * 
28254  * @constructor
28255  * Create a new NavProgressItem
28256  * @param {Object} config The config object
28257  */
28258 Roo.bootstrap.NavProgressItem = function(config){
28259     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28260     this.addEvents({
28261         // raw events
28262         /**
28263          * @event click
28264          * The raw click event for the entire grid.
28265          * @param {Roo.bootstrap.NavProgressItem} this
28266          * @param {Roo.EventObject} e
28267          */
28268         "click" : true
28269     });
28270    
28271 };
28272
28273 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28274     
28275     rid : '',
28276     active : false,
28277     disabled : false,
28278     html : '',
28279     position : 'bottom',
28280     icon : false,
28281     
28282     getAutoCreate : function()
28283     {
28284         var iconCls = 'roo-navigation-bar-item-icon';
28285         
28286         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28287         
28288         var cfg = {
28289             tag: 'li',
28290             cls: 'roo-navigation-bar-item',
28291             cn : [
28292                 {
28293                     tag : 'i',
28294                     cls : iconCls
28295                 }
28296             ]
28297         };
28298         
28299         if(this.active){
28300             cfg.cls += ' active';
28301         }
28302         if(this.disabled){
28303             cfg.cls += ' disabled';
28304         }
28305         
28306         return cfg;
28307     },
28308     
28309     disable : function()
28310     {
28311         this.setDisabled(true);
28312     },
28313     
28314     enable : function()
28315     {
28316         this.setDisabled(false);
28317     },
28318     
28319     initEvents: function() 
28320     {
28321         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28322         
28323         this.iconEl.on('click', this.onClick, this);
28324     },
28325     
28326     onClick : function(e)
28327     {
28328         e.preventDefault();
28329         
28330         if(this.disabled){
28331             return;
28332         }
28333         
28334         if(this.fireEvent('click', this, e) === false){
28335             return;
28336         };
28337         
28338         this.parent().setActiveItem(this);
28339     },
28340     
28341     isActive: function () 
28342     {
28343         return this.active;
28344     },
28345     
28346     setActive : function(state)
28347     {
28348         if(this.active == state){
28349             return;
28350         }
28351         
28352         this.active = state;
28353         
28354         if (state) {
28355             this.el.addClass('active');
28356             return;
28357         }
28358         
28359         this.el.removeClass('active');
28360         
28361         return;
28362     },
28363     
28364     setDisabled : function(state)
28365     {
28366         if(this.disabled == state){
28367             return;
28368         }
28369         
28370         this.disabled = state;
28371         
28372         if (state) {
28373             this.el.addClass('disabled');
28374             return;
28375         }
28376         
28377         this.el.removeClass('disabled');
28378     },
28379     
28380     tooltipEl : function()
28381     {
28382         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28383     }
28384 });
28385  
28386
28387  /*
28388  * - LGPL
28389  *
28390  * FieldLabel
28391  * 
28392  */
28393
28394 /**
28395  * @class Roo.bootstrap.FieldLabel
28396  * @extends Roo.bootstrap.Component
28397  * Bootstrap FieldLabel class
28398  * @cfg {String} html contents of the element
28399  * @cfg {String} tag tag of the element default label
28400  * @cfg {String} cls class of the element
28401  * @cfg {String} target label target 
28402  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28403  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28404  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28405  * @cfg {String} iconTooltip default "This field is required"
28406  * 
28407  * @constructor
28408  * Create a new FieldLabel
28409  * @param {Object} config The config object
28410  */
28411
28412 Roo.bootstrap.FieldLabel = function(config){
28413     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28414     
28415     this.addEvents({
28416             /**
28417              * @event invalid
28418              * Fires after the field has been marked as invalid.
28419              * @param {Roo.form.FieldLabel} this
28420              * @param {String} msg The validation message
28421              */
28422             invalid : true,
28423             /**
28424              * @event valid
28425              * Fires after the field has been validated with no errors.
28426              * @param {Roo.form.FieldLabel} this
28427              */
28428             valid : true
28429         });
28430 };
28431
28432 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28433     
28434     tag: 'label',
28435     cls: '',
28436     html: '',
28437     target: '',
28438     allowBlank : true,
28439     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28440     validClass : 'text-success fa fa-lg fa-check',
28441     iconTooltip : 'This field is required',
28442     
28443     getAutoCreate : function(){
28444         
28445         var cfg = {
28446             tag : this.tag,
28447             cls : 'roo-bootstrap-field-label ' + this.cls,
28448             for : this.target,
28449             cn : [
28450                 {
28451                     tag : 'i',
28452                     cls : '',
28453                     tooltip : this.iconTooltip
28454                 },
28455                 {
28456                     tag : 'span',
28457                     html : this.html
28458                 }
28459             ] 
28460         };
28461         
28462         return cfg;
28463     },
28464     
28465     initEvents: function() 
28466     {
28467         Roo.bootstrap.Element.superclass.initEvents.call(this);
28468         
28469         this.iconEl = this.el.select('i', true).first();
28470         
28471         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28472         
28473         Roo.bootstrap.FieldLabel.register(this);
28474     },
28475     
28476     /**
28477      * Mark this field as valid
28478      */
28479     markValid : function()
28480     {
28481         this.iconEl.show();
28482         
28483         this.iconEl.removeClass(this.invalidClass);
28484         
28485         this.iconEl.addClass(this.validClass);
28486         
28487         this.fireEvent('valid', this);
28488     },
28489     
28490     /**
28491      * Mark this field as invalid
28492      * @param {String} msg The validation message
28493      */
28494     markInvalid : function(msg)
28495     {
28496         this.iconEl.show();
28497         
28498         this.iconEl.removeClass(this.validClass);
28499         
28500         this.iconEl.addClass(this.invalidClass);
28501         
28502         this.fireEvent('invalid', this, msg);
28503     }
28504     
28505    
28506 });
28507
28508 Roo.apply(Roo.bootstrap.FieldLabel, {
28509     
28510     groups: {},
28511     
28512      /**
28513     * register a FieldLabel Group
28514     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28515     */
28516     register : function(label)
28517     {
28518         if(this.groups.hasOwnProperty(label.target)){
28519             return;
28520         }
28521      
28522         this.groups[label.target] = label;
28523         
28524     },
28525     /**
28526     * fetch a FieldLabel Group based on the target
28527     * @param {string} target
28528     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28529     */
28530     get: function(target) {
28531         if (typeof(this.groups[target]) == 'undefined') {
28532             return false;
28533         }
28534         
28535         return this.groups[target] ;
28536     }
28537 });
28538
28539  
28540
28541  /*
28542  * - LGPL
28543  *
28544  * page DateSplitField.
28545  * 
28546  */
28547
28548
28549 /**
28550  * @class Roo.bootstrap.DateSplitField
28551  * @extends Roo.bootstrap.Component
28552  * Bootstrap DateSplitField class
28553  * @cfg {string} fieldLabel - the label associated
28554  * @cfg {Number} labelWidth set the width of label (0-12)
28555  * @cfg {String} labelAlign (top|left)
28556  * @cfg {Boolean} dayAllowBlank (true|false) default false
28557  * @cfg {Boolean} monthAllowBlank (true|false) default false
28558  * @cfg {Boolean} yearAllowBlank (true|false) default false
28559  * @cfg {string} dayPlaceholder 
28560  * @cfg {string} monthPlaceholder
28561  * @cfg {string} yearPlaceholder
28562  * @cfg {string} dayFormat default 'd'
28563  * @cfg {string} monthFormat default 'm'
28564  * @cfg {string} yearFormat default 'Y'
28565
28566  *     
28567  * @constructor
28568  * Create a new DateSplitField
28569  * @param {Object} config The config object
28570  */
28571
28572 Roo.bootstrap.DateSplitField = function(config){
28573     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28574     
28575     this.addEvents({
28576         // raw events
28577          /**
28578          * @event years
28579          * getting the data of years
28580          * @param {Roo.bootstrap.DateSplitField} this
28581          * @param {Object} years
28582          */
28583         "years" : true,
28584         /**
28585          * @event days
28586          * getting the data of days
28587          * @param {Roo.bootstrap.DateSplitField} this
28588          * @param {Object} days
28589          */
28590         "days" : true,
28591         /**
28592          * @event invalid
28593          * Fires after the field has been marked as invalid.
28594          * @param {Roo.form.Field} this
28595          * @param {String} msg The validation message
28596          */
28597         invalid : true,
28598        /**
28599          * @event valid
28600          * Fires after the field has been validated with no errors.
28601          * @param {Roo.form.Field} this
28602          */
28603         valid : true
28604     });
28605 };
28606
28607 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28608     
28609     fieldLabel : '',
28610     labelAlign : 'top',
28611     labelWidth : 3,
28612     dayAllowBlank : false,
28613     monthAllowBlank : false,
28614     yearAllowBlank : false,
28615     dayPlaceholder : '',
28616     monthPlaceholder : '',
28617     yearPlaceholder : '',
28618     dayFormat : 'd',
28619     monthFormat : 'm',
28620     yearFormat : 'Y',
28621     isFormField : true,
28622     
28623     getAutoCreate : function()
28624     {
28625         var cfg = {
28626             tag : 'div',
28627             cls : 'row roo-date-split-field-group',
28628             cn : [
28629                 {
28630                     tag : 'input',
28631                     type : 'hidden',
28632                     cls : 'form-hidden-field roo-date-split-field-group-value',
28633                     name : this.name
28634                 }
28635             ]
28636         };
28637         
28638         if(this.fieldLabel){
28639             cfg.cn.push({
28640                 tag : 'div',
28641                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28642                 cn : [
28643                     {
28644                         tag : 'label',
28645                         html : this.fieldLabel
28646                     }
28647                 ]
28648             });
28649         }
28650         
28651         Roo.each(['day', 'month', 'year'], function(t){
28652             cfg.cn.push({
28653                 tag : 'div',
28654                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28655             });
28656         }, this);
28657         
28658         return cfg;
28659     },
28660     
28661     inputEl: function ()
28662     {
28663         return this.el.select('.roo-date-split-field-group-value', true).first();
28664     },
28665     
28666     onRender : function(ct, position) 
28667     {
28668         var _this = this;
28669         
28670         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28671         
28672         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28673         
28674         this.dayField = new Roo.bootstrap.ComboBox({
28675             allowBlank : this.dayAllowBlank,
28676             alwaysQuery : true,
28677             displayField : 'value',
28678             editable : false,
28679             fieldLabel : '',
28680             forceSelection : true,
28681             mode : 'local',
28682             placeholder : this.dayPlaceholder,
28683             selectOnFocus : true,
28684             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28685             triggerAction : 'all',
28686             typeAhead : true,
28687             valueField : 'value',
28688             store : new Roo.data.SimpleStore({
28689                 data : (function() {    
28690                     var days = [];
28691                     _this.fireEvent('days', _this, days);
28692                     return days;
28693                 })(),
28694                 fields : [ 'value' ]
28695             }),
28696             listeners : {
28697                 select : function (_self, record, index)
28698                 {
28699                     _this.setValue(_this.getValue());
28700                 }
28701             }
28702         });
28703
28704         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28705         
28706         this.monthField = new Roo.bootstrap.MonthField({
28707             after : '<i class=\"fa fa-calendar\"></i>',
28708             allowBlank : this.monthAllowBlank,
28709             placeholder : this.monthPlaceholder,
28710             readOnly : true,
28711             listeners : {
28712                 render : function (_self)
28713                 {
28714                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28715                         e.preventDefault();
28716                         _self.focus();
28717                     });
28718                 },
28719                 select : function (_self, oldvalue, newvalue)
28720                 {
28721                     _this.setValue(_this.getValue());
28722                 }
28723             }
28724         });
28725         
28726         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28727         
28728         this.yearField = new Roo.bootstrap.ComboBox({
28729             allowBlank : this.yearAllowBlank,
28730             alwaysQuery : true,
28731             displayField : 'value',
28732             editable : false,
28733             fieldLabel : '',
28734             forceSelection : true,
28735             mode : 'local',
28736             placeholder : this.yearPlaceholder,
28737             selectOnFocus : true,
28738             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28739             triggerAction : 'all',
28740             typeAhead : true,
28741             valueField : 'value',
28742             store : new Roo.data.SimpleStore({
28743                 data : (function() {
28744                     var years = [];
28745                     _this.fireEvent('years', _this, years);
28746                     return years;
28747                 })(),
28748                 fields : [ 'value' ]
28749             }),
28750             listeners : {
28751                 select : function (_self, record, index)
28752                 {
28753                     _this.setValue(_this.getValue());
28754                 }
28755             }
28756         });
28757
28758         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28759     },
28760     
28761     setValue : function(v, format)
28762     {
28763         this.inputEl.dom.value = v;
28764         
28765         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28766         
28767         var d = Date.parseDate(v, f);
28768         
28769         if(!d){
28770             this.validate();
28771             return;
28772         }
28773         
28774         this.setDay(d.format(this.dayFormat));
28775         this.setMonth(d.format(this.monthFormat));
28776         this.setYear(d.format(this.yearFormat));
28777         
28778         this.validate();
28779         
28780         return;
28781     },
28782     
28783     setDay : function(v)
28784     {
28785         this.dayField.setValue(v);
28786         this.inputEl.dom.value = this.getValue();
28787         this.validate();
28788         return;
28789     },
28790     
28791     setMonth : function(v)
28792     {
28793         this.monthField.setValue(v, true);
28794         this.inputEl.dom.value = this.getValue();
28795         this.validate();
28796         return;
28797     },
28798     
28799     setYear : function(v)
28800     {
28801         this.yearField.setValue(v);
28802         this.inputEl.dom.value = this.getValue();
28803         this.validate();
28804         return;
28805     },
28806     
28807     getDay : function()
28808     {
28809         return this.dayField.getValue();
28810     },
28811     
28812     getMonth : function()
28813     {
28814         return this.monthField.getValue();
28815     },
28816     
28817     getYear : function()
28818     {
28819         return this.yearField.getValue();
28820     },
28821     
28822     getValue : function()
28823     {
28824         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28825         
28826         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28827         
28828         return date;
28829     },
28830     
28831     reset : function()
28832     {
28833         this.setDay('');
28834         this.setMonth('');
28835         this.setYear('');
28836         this.inputEl.dom.value = '';
28837         this.validate();
28838         return;
28839     },
28840     
28841     validate : function()
28842     {
28843         var d = this.dayField.validate();
28844         var m = this.monthField.validate();
28845         var y = this.yearField.validate();
28846         
28847         var valid = true;
28848         
28849         if(
28850                 (!this.dayAllowBlank && !d) ||
28851                 (!this.monthAllowBlank && !m) ||
28852                 (!this.yearAllowBlank && !y)
28853         ){
28854             valid = false;
28855         }
28856         
28857         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28858             return valid;
28859         }
28860         
28861         if(valid){
28862             this.markValid();
28863             return valid;
28864         }
28865         
28866         this.markInvalid();
28867         
28868         return valid;
28869     },
28870     
28871     markValid : function()
28872     {
28873         
28874         var label = this.el.select('label', true).first();
28875         var icon = this.el.select('i.fa-star', true).first();
28876
28877         if(label && icon){
28878             icon.remove();
28879         }
28880         
28881         this.fireEvent('valid', this);
28882     },
28883     
28884      /**
28885      * Mark this field as invalid
28886      * @param {String} msg The validation message
28887      */
28888     markInvalid : function(msg)
28889     {
28890         
28891         var label = this.el.select('label', true).first();
28892         var icon = this.el.select('i.fa-star', true).first();
28893
28894         if(label && !icon){
28895             this.el.select('.roo-date-split-field-label', true).createChild({
28896                 tag : 'i',
28897                 cls : 'text-danger fa fa-lg fa-star',
28898                 tooltip : 'This field is required',
28899                 style : 'margin-right:5px;'
28900             }, label, true);
28901         }
28902         
28903         this.fireEvent('invalid', this, msg);
28904     },
28905     
28906     clearInvalid : function()
28907     {
28908         var label = this.el.select('label', true).first();
28909         var icon = this.el.select('i.fa-star', true).first();
28910
28911         if(label && icon){
28912             icon.remove();
28913         }
28914         
28915         this.fireEvent('valid', this);
28916     },
28917     
28918     getName: function()
28919     {
28920         return this.name;
28921     }
28922     
28923 });
28924
28925  /**
28926  *
28927  * This is based on 
28928  * http://masonry.desandro.com
28929  *
28930  * The idea is to render all the bricks based on vertical width...
28931  *
28932  * The original code extends 'outlayer' - we might need to use that....
28933  * 
28934  */
28935
28936
28937 /**
28938  * @class Roo.bootstrap.LayoutMasonry
28939  * @extends Roo.bootstrap.Component
28940  * Bootstrap Layout Masonry class
28941  * 
28942  * @constructor
28943  * Create a new Element
28944  * @param {Object} config The config object
28945  */
28946
28947 Roo.bootstrap.LayoutMasonry = function(config){
28948     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28949     
28950     this.bricks = [];
28951     
28952 };
28953
28954 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28955     
28956     /**
28957      * @cfg {Boolean} isLayoutInstant = no animation?
28958      */   
28959     isLayoutInstant : false, // needed?
28960    
28961     /**
28962      * @cfg {Number} boxWidth  width of the columns
28963      */   
28964     boxWidth : 450,
28965     
28966       /**
28967      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28968      */   
28969     boxHeight : 0,
28970     
28971     /**
28972      * @cfg {Number} padWidth padding below box..
28973      */   
28974     padWidth : 10, 
28975     
28976     /**
28977      * @cfg {Number} gutter gutter width..
28978      */   
28979     gutter : 10,
28980     
28981      /**
28982      * @cfg {Number} maxCols maximum number of columns
28983      */   
28984     
28985     maxCols: 0,
28986     
28987     /**
28988      * @cfg {Boolean} isAutoInitial defalut true
28989      */   
28990     isAutoInitial : true, 
28991     
28992     containerWidth: 0,
28993     
28994     /**
28995      * @cfg {Boolean} isHorizontal defalut false
28996      */   
28997     isHorizontal : false, 
28998
28999     currentSize : null,
29000     
29001     tag: 'div',
29002     
29003     cls: '',
29004     
29005     bricks: null, //CompositeElement
29006     
29007     cols : 1,
29008     
29009     _isLayoutInited : false,
29010     
29011 //    isAlternative : false, // only use for vertical layout...
29012     
29013     /**
29014      * @cfg {Number} alternativePadWidth padding below box..
29015      */   
29016     alternativePadWidth : 50, 
29017     
29018     getAutoCreate : function(){
29019         
29020         var cfg = {
29021             tag: this.tag,
29022             cls: 'blog-masonary-wrapper ' + this.cls,
29023             cn : {
29024                 cls : 'mas-boxes masonary'
29025             }
29026         };
29027         
29028         return cfg;
29029     },
29030     
29031     getChildContainer: function( )
29032     {
29033         if (this.boxesEl) {
29034             return this.boxesEl;
29035         }
29036         
29037         this.boxesEl = this.el.select('.mas-boxes').first();
29038         
29039         return this.boxesEl;
29040     },
29041     
29042     
29043     initEvents : function()
29044     {
29045         var _this = this;
29046         
29047         if(this.isAutoInitial){
29048             Roo.log('hook children rendered');
29049             this.on('childrenrendered', function() {
29050                 Roo.log('children rendered');
29051                 _this.initial();
29052             } ,this);
29053         }
29054     },
29055     
29056     initial : function()
29057     {
29058         this.currentSize = this.el.getBox(true);
29059         
29060         Roo.EventManager.onWindowResize(this.resize, this); 
29061
29062         if(!this.isAutoInitial){
29063             this.layout();
29064             return;
29065         }
29066         
29067         this.layout();
29068         
29069         return;
29070         //this.layout.defer(500,this);
29071         
29072     },
29073     
29074     resize : function()
29075     {
29076         Roo.log('resize');
29077         
29078         var cs = this.el.getBox(true);
29079         
29080         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29081             Roo.log("no change in with or X");
29082             return;
29083         }
29084         
29085         this.currentSize = cs;
29086         
29087         this.layout();
29088         
29089     },
29090     
29091     layout : function()
29092     {   
29093         this._resetLayout();
29094         
29095         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29096         
29097         this.layoutItems( isInstant );
29098       
29099         this._isLayoutInited = true;
29100         
29101     },
29102     
29103     _resetLayout : function()
29104     {
29105         if(this.isHorizontal){
29106             this.horizontalMeasureColumns();
29107             return;
29108         }
29109         
29110         this.verticalMeasureColumns();
29111         
29112     },
29113     
29114     verticalMeasureColumns : function()
29115     {
29116         this.getContainerWidth();
29117         
29118 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29119 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29120 //            return;
29121 //        }
29122         
29123         var boxWidth = this.boxWidth + this.padWidth;
29124         
29125         if(this.containerWidth < this.boxWidth){
29126             boxWidth = this.containerWidth
29127         }
29128         
29129         var containerWidth = this.containerWidth;
29130         
29131         var cols = Math.floor(containerWidth / boxWidth);
29132         
29133         this.cols = Math.max( cols, 1 );
29134         
29135         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29136         
29137         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29138         
29139         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29140         
29141         this.colWidth = boxWidth + avail - this.padWidth;
29142         
29143         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29144         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29145     },
29146     
29147     horizontalMeasureColumns : function()
29148     {
29149         this.getContainerWidth();
29150         
29151         var boxWidth = this.boxWidth;
29152         
29153         if(this.containerWidth < boxWidth){
29154             boxWidth = this.containerWidth;
29155         }
29156         
29157         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29158         
29159         this.el.setHeight(boxWidth);
29160         
29161     },
29162     
29163     getContainerWidth : function()
29164     {
29165         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29166     },
29167     
29168     layoutItems : function( isInstant )
29169     {
29170         var items = Roo.apply([], this.bricks);
29171         
29172         if(this.isHorizontal){
29173             this._horizontalLayoutItems( items , isInstant );
29174             return;
29175         }
29176         
29177 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29178 //            this._verticalAlternativeLayoutItems( items , isInstant );
29179 //            return;
29180 //        }
29181         
29182         this._verticalLayoutItems( items , isInstant );
29183         
29184     },
29185     
29186     _verticalLayoutItems : function ( items , isInstant)
29187     {
29188         if ( !items || !items.length ) {
29189             return;
29190         }
29191         
29192         var standard = [
29193             ['xs', 'xs', 'xs', 'tall'],
29194             ['xs', 'xs', 'tall'],
29195             ['xs', 'xs', 'sm'],
29196             ['xs', 'xs', 'xs'],
29197             ['xs', 'tall'],
29198             ['xs', 'sm'],
29199             ['xs', 'xs'],
29200             ['xs'],
29201             
29202             ['sm', 'xs', 'xs'],
29203             ['sm', 'xs'],
29204             ['sm'],
29205             
29206             ['tall', 'xs', 'xs', 'xs'],
29207             ['tall', 'xs', 'xs'],
29208             ['tall', 'xs'],
29209             ['tall']
29210             
29211         ];
29212         
29213         var queue = [];
29214         
29215         var boxes = [];
29216         
29217         var box = [];
29218         
29219         Roo.each(items, function(item, k){
29220             
29221             switch (item.size) {
29222                 // these layouts take up a full box,
29223                 case 'md' :
29224                 case 'md-left' :
29225                 case 'md-right' :
29226                 case 'wide' :
29227                     
29228                     if(box.length){
29229                         boxes.push(box);
29230                         box = [];
29231                     }
29232                     
29233                     boxes.push([item]);
29234                     
29235                     break;
29236                     
29237                 case 'xs' :
29238                 case 'sm' :
29239                 case 'tall' :
29240                     
29241                     box.push(item);
29242                     
29243                     break;
29244                 default :
29245                     break;
29246                     
29247             }
29248             
29249         }, this);
29250         
29251         if(box.length){
29252             boxes.push(box);
29253             box = [];
29254         }
29255         
29256         var filterPattern = function(box, length)
29257         {
29258             if(!box.length){
29259                 return;
29260             }
29261             
29262             var match = false;
29263             
29264             var pattern = box.slice(0, length);
29265             
29266             var format = [];
29267             
29268             Roo.each(pattern, function(i){
29269                 format.push(i.size);
29270             }, this);
29271             
29272             Roo.each(standard, function(s){
29273                 
29274                 if(String(s) != String(format)){
29275                     return;
29276                 }
29277                 
29278                 match = true;
29279                 return false;
29280                 
29281             }, this);
29282             
29283             if(!match && length == 1){
29284                 return;
29285             }
29286             
29287             if(!match){
29288                 filterPattern(box, length - 1);
29289                 return;
29290             }
29291                 
29292             queue.push(pattern);
29293
29294             box = box.slice(length, box.length);
29295
29296             filterPattern(box, 4);
29297
29298             return;
29299             
29300         }
29301         
29302         Roo.each(boxes, function(box, k){
29303             
29304             if(!box.length){
29305                 return;
29306             }
29307             
29308             if(box.length == 1){
29309                 queue.push(box);
29310                 return;
29311             }
29312             
29313             filterPattern(box, 4);
29314             
29315         }, this);
29316         
29317         this._processVerticalLayoutQueue( queue, isInstant );
29318         
29319     },
29320     
29321 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29322 //    {
29323 //        if ( !items || !items.length ) {
29324 //            return;
29325 //        }
29326 //
29327 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29328 //        
29329 //    },
29330     
29331     _horizontalLayoutItems : function ( items , isInstant)
29332     {
29333         if ( !items || !items.length || items.length < 3) {
29334             return;
29335         }
29336         
29337         items.reverse();
29338         
29339         var eItems = items.slice(0, 3);
29340         
29341         items = items.slice(3, items.length);
29342         
29343         var standard = [
29344             ['xs', 'xs', 'xs', 'wide'],
29345             ['xs', 'xs', 'wide'],
29346             ['xs', 'xs', 'sm'],
29347             ['xs', 'xs', 'xs'],
29348             ['xs', 'wide'],
29349             ['xs', 'sm'],
29350             ['xs', 'xs'],
29351             ['xs'],
29352             
29353             ['sm', 'xs', 'xs'],
29354             ['sm', 'xs'],
29355             ['sm'],
29356             
29357             ['wide', 'xs', 'xs', 'xs'],
29358             ['wide', 'xs', 'xs'],
29359             ['wide', 'xs'],
29360             ['wide'],
29361             
29362             ['wide-thin']
29363         ];
29364         
29365         var queue = [];
29366         
29367         var boxes = [];
29368         
29369         var box = [];
29370         
29371         Roo.each(items, function(item, k){
29372             
29373             switch (item.size) {
29374                 case 'md' :
29375                 case 'md-left' :
29376                 case 'md-right' :
29377                 case 'tall' :
29378                     
29379                     if(box.length){
29380                         boxes.push(box);
29381                         box = [];
29382                     }
29383                     
29384                     boxes.push([item]);
29385                     
29386                     break;
29387                     
29388                 case 'xs' :
29389                 case 'sm' :
29390                 case 'wide' :
29391                 case 'wide-thin' :
29392                     
29393                     box.push(item);
29394                     
29395                     break;
29396                 default :
29397                     break;
29398                     
29399             }
29400             
29401         }, this);
29402         
29403         if(box.length){
29404             boxes.push(box);
29405             box = [];
29406         }
29407         
29408         var filterPattern = function(box, length)
29409         {
29410             if(!box.length){
29411                 return;
29412             }
29413             
29414             var match = false;
29415             
29416             var pattern = box.slice(0, length);
29417             
29418             var format = [];
29419             
29420             Roo.each(pattern, function(i){
29421                 format.push(i.size);
29422             }, this);
29423             
29424             Roo.each(standard, function(s){
29425                 
29426                 if(String(s) != String(format)){
29427                     return;
29428                 }
29429                 
29430                 match = true;
29431                 return false;
29432                 
29433             }, this);
29434             
29435             if(!match && length == 1){
29436                 return;
29437             }
29438             
29439             if(!match){
29440                 filterPattern(box, length - 1);
29441                 return;
29442             }
29443                 
29444             queue.push(pattern);
29445
29446             box = box.slice(length, box.length);
29447
29448             filterPattern(box, 4);
29449
29450             return;
29451             
29452         }
29453         
29454         Roo.each(boxes, function(box, k){
29455             
29456             if(!box.length){
29457                 return;
29458             }
29459             
29460             if(box.length == 1){
29461                 queue.push(box);
29462                 return;
29463             }
29464             
29465             filterPattern(box, 4);
29466             
29467         }, this);
29468         
29469         
29470         var prune = [];
29471         
29472         var pos = this.el.getBox(true);
29473         
29474         var minX = pos.x;
29475         
29476         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29477         
29478         var hit_end = false;
29479         
29480         Roo.each(queue, function(box){
29481             
29482             if(hit_end){
29483                 
29484                 Roo.each(box, function(b){
29485                 
29486                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29487                     b.el.hide();
29488
29489                 }, this);
29490
29491                 return;
29492             }
29493             
29494             var mx = 0;
29495             
29496             Roo.each(box, function(b){
29497                 
29498                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29499                 b.el.show();
29500
29501                 mx = Math.max(mx, b.x);
29502                 
29503             }, this);
29504             
29505             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29506             
29507             if(maxX < minX){
29508                 
29509                 Roo.each(box, function(b){
29510                 
29511                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29512                     b.el.hide();
29513                     
29514                 }, this);
29515                 
29516                 hit_end = true;
29517                 
29518                 return;
29519             }
29520             
29521             prune.push(box);
29522             
29523         }, this);
29524         
29525         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29526     },
29527     
29528     /** Sets position of item in DOM
29529     * @param {Element} item
29530     * @param {Number} x - horizontal position
29531     * @param {Number} y - vertical position
29532     * @param {Boolean} isInstant - disables transitions
29533     */
29534     _processVerticalLayoutQueue : function( queue, isInstant )
29535     {
29536         var pos = this.el.getBox(true);
29537         var x = pos.x;
29538         var y = pos.y;
29539         var maxY = [];
29540         
29541         for (var i = 0; i < this.cols; i++){
29542             maxY[i] = pos.y;
29543         }
29544         
29545         Roo.each(queue, function(box, k){
29546             
29547             var col = k % this.cols;
29548             
29549             Roo.each(box, function(b,kk){
29550                 
29551                 b.el.position('absolute');
29552                 
29553                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29554                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29555                 
29556                 if(b.size == 'md-left' || b.size == 'md-right'){
29557                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29558                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29559                 }
29560                 
29561                 b.el.setWidth(width);
29562                 b.el.setHeight(height);
29563                 // iframe?
29564                 b.el.select('iframe',true).setSize(width,height);
29565                 
29566             }, this);
29567             
29568             for (var i = 0; i < this.cols; i++){
29569                 
29570                 if(maxY[i] < maxY[col]){
29571                     col = i;
29572                     continue;
29573                 }
29574                 
29575                 col = Math.min(col, i);
29576                 
29577             }
29578             
29579             x = pos.x + col * (this.colWidth + this.padWidth);
29580             
29581             y = maxY[col];
29582             
29583             var positions = [];
29584             
29585             switch (box.length){
29586                 case 1 :
29587                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29588                     break;
29589                 case 2 :
29590                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29591                     break;
29592                 case 3 :
29593                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29594                     break;
29595                 case 4 :
29596                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29597                     break;
29598                 default :
29599                     break;
29600             }
29601             
29602             Roo.each(box, function(b,kk){
29603                 
29604                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29605                 
29606                 var sz = b.el.getSize();
29607                 
29608                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29609                 
29610             }, this);
29611             
29612         }, this);
29613         
29614         var mY = 0;
29615         
29616         for (var i = 0; i < this.cols; i++){
29617             mY = Math.max(mY, maxY[i]);
29618         }
29619         
29620         this.el.setHeight(mY - pos.y);
29621         
29622     },
29623     
29624 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29625 //    {
29626 //        var pos = this.el.getBox(true);
29627 //        var x = pos.x;
29628 //        var y = pos.y;
29629 //        var maxX = pos.right;
29630 //        
29631 //        var maxHeight = 0;
29632 //        
29633 //        Roo.each(items, function(item, k){
29634 //            
29635 //            var c = k % 2;
29636 //            
29637 //            item.el.position('absolute');
29638 //                
29639 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29640 //
29641 //            item.el.setWidth(width);
29642 //
29643 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29644 //
29645 //            item.el.setHeight(height);
29646 //            
29647 //            if(c == 0){
29648 //                item.el.setXY([x, y], isInstant ? false : true);
29649 //            } else {
29650 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29651 //            }
29652 //            
29653 //            y = y + height + this.alternativePadWidth;
29654 //            
29655 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29656 //            
29657 //        }, this);
29658 //        
29659 //        this.el.setHeight(maxHeight);
29660 //        
29661 //    },
29662     
29663     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29664     {
29665         var pos = this.el.getBox(true);
29666         
29667         var minX = pos.x;
29668         var minY = pos.y;
29669         
29670         var maxX = pos.right;
29671         
29672         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29673         
29674         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29675         
29676         Roo.each(queue, function(box, k){
29677             
29678             Roo.each(box, function(b, kk){
29679                 
29680                 b.el.position('absolute');
29681                 
29682                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29683                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29684                 
29685                 if(b.size == 'md-left' || b.size == 'md-right'){
29686                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29687                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29688                 }
29689                 
29690                 b.el.setWidth(width);
29691                 b.el.setHeight(height);
29692                 
29693             }, this);
29694             
29695             if(!box.length){
29696                 return;
29697             }
29698             
29699             var positions = [];
29700             
29701             switch (box.length){
29702                 case 1 :
29703                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29704                     break;
29705                 case 2 :
29706                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29707                     break;
29708                 case 3 :
29709                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29710                     break;
29711                 case 4 :
29712                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29713                     break;
29714                 default :
29715                     break;
29716             }
29717             
29718             Roo.each(box, function(b,kk){
29719                 
29720                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29721                 
29722                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29723                 
29724             }, this);
29725             
29726         }, this);
29727         
29728     },
29729     
29730     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29731     {
29732         Roo.each(eItems, function(b,k){
29733             
29734             b.size = (k == 0) ? 'sm' : 'xs';
29735             b.x = (k == 0) ? 2 : 1;
29736             b.y = (k == 0) ? 2 : 1;
29737             
29738             b.el.position('absolute');
29739             
29740             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29741                 
29742             b.el.setWidth(width);
29743             
29744             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29745             
29746             b.el.setHeight(height);
29747             
29748         }, this);
29749
29750         var positions = [];
29751         
29752         positions.push({
29753             x : maxX - this.unitWidth * 2 - this.gutter,
29754             y : minY
29755         });
29756         
29757         positions.push({
29758             x : maxX - this.unitWidth,
29759             y : minY + (this.unitWidth + this.gutter) * 2
29760         });
29761         
29762         positions.push({
29763             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29764             y : minY
29765         });
29766         
29767         Roo.each(eItems, function(b,k){
29768             
29769             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29770
29771         }, this);
29772         
29773     },
29774     
29775     getVerticalOneBoxColPositions : function(x, y, box)
29776     {
29777         var pos = [];
29778         
29779         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29780         
29781         if(box[0].size == 'md-left'){
29782             rand = 0;
29783         }
29784         
29785         if(box[0].size == 'md-right'){
29786             rand = 1;
29787         }
29788         
29789         pos.push({
29790             x : x + (this.unitWidth + this.gutter) * rand,
29791             y : y
29792         });
29793         
29794         return pos;
29795     },
29796     
29797     getVerticalTwoBoxColPositions : function(x, y, box)
29798     {
29799         var pos = [];
29800         
29801         if(box[0].size == 'xs'){
29802             
29803             pos.push({
29804                 x : x,
29805                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29806             });
29807
29808             pos.push({
29809                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29810                 y : y
29811             });
29812             
29813             return pos;
29814             
29815         }
29816         
29817         pos.push({
29818             x : x,
29819             y : y
29820         });
29821
29822         pos.push({
29823             x : x + (this.unitWidth + this.gutter) * 2,
29824             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29825         });
29826         
29827         return pos;
29828         
29829     },
29830     
29831     getVerticalThreeBoxColPositions : function(x, y, box)
29832     {
29833         var pos = [];
29834         
29835         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29836             
29837             pos.push({
29838                 x : x,
29839                 y : y
29840             });
29841
29842             pos.push({
29843                 x : x + (this.unitWidth + this.gutter) * 1,
29844                 y : y
29845             });
29846             
29847             pos.push({
29848                 x : x + (this.unitWidth + this.gutter) * 2,
29849                 y : y
29850             });
29851             
29852             return pos;
29853             
29854         }
29855         
29856         if(box[0].size == 'xs' && box[1].size == 'xs'){
29857             
29858             pos.push({
29859                 x : x,
29860                 y : y
29861             });
29862
29863             pos.push({
29864                 x : x,
29865                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29866             });
29867             
29868             pos.push({
29869                 x : x + (this.unitWidth + this.gutter) * 1,
29870                 y : y
29871             });
29872             
29873             return pos;
29874             
29875         }
29876         
29877         pos.push({
29878             x : x,
29879             y : y
29880         });
29881
29882         pos.push({
29883             x : x + (this.unitWidth + this.gutter) * 2,
29884             y : y
29885         });
29886
29887         pos.push({
29888             x : x + (this.unitWidth + this.gutter) * 2,
29889             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29890         });
29891             
29892         return pos;
29893         
29894     },
29895     
29896     getVerticalFourBoxColPositions : function(x, y, box)
29897     {
29898         var pos = [];
29899         
29900         if(box[0].size == 'xs'){
29901             
29902             pos.push({
29903                 x : x,
29904                 y : y
29905             });
29906
29907             pos.push({
29908                 x : x,
29909                 y : y + (this.unitHeight + this.gutter) * 1
29910             });
29911             
29912             pos.push({
29913                 x : x,
29914                 y : y + (this.unitHeight + this.gutter) * 2
29915             });
29916             
29917             pos.push({
29918                 x : x + (this.unitWidth + this.gutter) * 1,
29919                 y : y
29920             });
29921             
29922             return pos;
29923             
29924         }
29925         
29926         pos.push({
29927             x : x,
29928             y : y
29929         });
29930
29931         pos.push({
29932             x : x + (this.unitWidth + this.gutter) * 2,
29933             y : y
29934         });
29935
29936         pos.push({
29937             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29938             y : y + (this.unitHeight + this.gutter) * 1
29939         });
29940
29941         pos.push({
29942             x : x + (this.unitWidth + this.gutter) * 2,
29943             y : y + (this.unitWidth + this.gutter) * 2
29944         });
29945
29946         return pos;
29947         
29948     },
29949     
29950     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29951     {
29952         var pos = [];
29953         
29954         if(box[0].size == 'md-left'){
29955             pos.push({
29956                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29957                 y : minY
29958             });
29959             
29960             return pos;
29961         }
29962         
29963         if(box[0].size == 'md-right'){
29964             pos.push({
29965                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29966                 y : minY + (this.unitWidth + this.gutter) * 1
29967             });
29968             
29969             return pos;
29970         }
29971         
29972         var rand = Math.floor(Math.random() * (4 - box[0].y));
29973         
29974         pos.push({
29975             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29976             y : minY + (this.unitWidth + this.gutter) * rand
29977         });
29978         
29979         return pos;
29980         
29981     },
29982     
29983     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29984     {
29985         var pos = [];
29986         
29987         if(box[0].size == 'xs'){
29988             
29989             pos.push({
29990                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29991                 y : minY
29992             });
29993
29994             pos.push({
29995                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29996                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29997             });
29998             
29999             return pos;
30000             
30001         }
30002         
30003         pos.push({
30004             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30005             y : minY
30006         });
30007
30008         pos.push({
30009             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30010             y : minY + (this.unitWidth + this.gutter) * 2
30011         });
30012         
30013         return pos;
30014         
30015     },
30016     
30017     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30018     {
30019         var pos = [];
30020         
30021         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30022             
30023             pos.push({
30024                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30025                 y : minY
30026             });
30027
30028             pos.push({
30029                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30030                 y : minY + (this.unitWidth + this.gutter) * 1
30031             });
30032             
30033             pos.push({
30034                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30035                 y : minY + (this.unitWidth + this.gutter) * 2
30036             });
30037             
30038             return pos;
30039             
30040         }
30041         
30042         if(box[0].size == 'xs' && box[1].size == 'xs'){
30043             
30044             pos.push({
30045                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30046                 y : minY
30047             });
30048
30049             pos.push({
30050                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30051                 y : minY
30052             });
30053             
30054             pos.push({
30055                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30056                 y : minY + (this.unitWidth + this.gutter) * 1
30057             });
30058             
30059             return pos;
30060             
30061         }
30062         
30063         pos.push({
30064             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30065             y : minY
30066         });
30067
30068         pos.push({
30069             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30070             y : minY + (this.unitWidth + this.gutter) * 2
30071         });
30072
30073         pos.push({
30074             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30075             y : minY + (this.unitWidth + this.gutter) * 2
30076         });
30077             
30078         return pos;
30079         
30080     },
30081     
30082     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30083     {
30084         var pos = [];
30085         
30086         if(box[0].size == 'xs'){
30087             
30088             pos.push({
30089                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30090                 y : minY
30091             });
30092
30093             pos.push({
30094                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30095                 y : minY
30096             });
30097             
30098             pos.push({
30099                 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),
30100                 y : minY
30101             });
30102             
30103             pos.push({
30104                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30105                 y : minY + (this.unitWidth + this.gutter) * 1
30106             });
30107             
30108             return pos;
30109             
30110         }
30111         
30112         pos.push({
30113             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30114             y : minY
30115         });
30116         
30117         pos.push({
30118             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30119             y : minY + (this.unitWidth + this.gutter) * 2
30120         });
30121         
30122         pos.push({
30123             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30124             y : minY + (this.unitWidth + this.gutter) * 2
30125         });
30126         
30127         pos.push({
30128             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),
30129             y : minY + (this.unitWidth + this.gutter) * 2
30130         });
30131
30132         return pos;
30133         
30134     }
30135     
30136 });
30137
30138  
30139
30140  /**
30141  *
30142  * This is based on 
30143  * http://masonry.desandro.com
30144  *
30145  * The idea is to render all the bricks based on vertical width...
30146  *
30147  * The original code extends 'outlayer' - we might need to use that....
30148  * 
30149  */
30150
30151
30152 /**
30153  * @class Roo.bootstrap.LayoutMasonryAuto
30154  * @extends Roo.bootstrap.Component
30155  * Bootstrap Layout Masonry class
30156  * 
30157  * @constructor
30158  * Create a new Element
30159  * @param {Object} config The config object
30160  */
30161
30162 Roo.bootstrap.LayoutMasonryAuto = function(config){
30163     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30164 };
30165
30166 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30167     
30168       /**
30169      * @cfg {Boolean} isFitWidth  - resize the width..
30170      */   
30171     isFitWidth : false,  // options..
30172     /**
30173      * @cfg {Boolean} isOriginLeft = left align?
30174      */   
30175     isOriginLeft : true,
30176     /**
30177      * @cfg {Boolean} isOriginTop = top align?
30178      */   
30179     isOriginTop : false,
30180     /**
30181      * @cfg {Boolean} isLayoutInstant = no animation?
30182      */   
30183     isLayoutInstant : false, // needed?
30184     /**
30185      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30186      */   
30187     isResizingContainer : true,
30188     /**
30189      * @cfg {Number} columnWidth  width of the columns 
30190      */   
30191     
30192     columnWidth : 0,
30193     
30194     /**
30195      * @cfg {Number} maxCols maximum number of columns
30196      */   
30197     
30198     maxCols: 0,
30199     /**
30200      * @cfg {Number} padHeight padding below box..
30201      */   
30202     
30203     padHeight : 10, 
30204     
30205     /**
30206      * @cfg {Boolean} isAutoInitial defalut true
30207      */   
30208     
30209     isAutoInitial : true, 
30210     
30211     // private?
30212     gutter : 0,
30213     
30214     containerWidth: 0,
30215     initialColumnWidth : 0,
30216     currentSize : null,
30217     
30218     colYs : null, // array.
30219     maxY : 0,
30220     padWidth: 10,
30221     
30222     
30223     tag: 'div',
30224     cls: '',
30225     bricks: null, //CompositeElement
30226     cols : 0, // array?
30227     // element : null, // wrapped now this.el
30228     _isLayoutInited : null, 
30229     
30230     
30231     getAutoCreate : function(){
30232         
30233         var cfg = {
30234             tag: this.tag,
30235             cls: 'blog-masonary-wrapper ' + this.cls,
30236             cn : {
30237                 cls : 'mas-boxes masonary'
30238             }
30239         };
30240         
30241         return cfg;
30242     },
30243     
30244     getChildContainer: function( )
30245     {
30246         if (this.boxesEl) {
30247             return this.boxesEl;
30248         }
30249         
30250         this.boxesEl = this.el.select('.mas-boxes').first();
30251         
30252         return this.boxesEl;
30253     },
30254     
30255     
30256     initEvents : function()
30257     {
30258         var _this = this;
30259         
30260         if(this.isAutoInitial){
30261             Roo.log('hook children rendered');
30262             this.on('childrenrendered', function() {
30263                 Roo.log('children rendered');
30264                 _this.initial();
30265             } ,this);
30266         }
30267         
30268     },
30269     
30270     initial : function()
30271     {
30272         this.reloadItems();
30273
30274         this.currentSize = this.el.getBox(true);
30275
30276         /// was window resize... - let's see if this works..
30277         Roo.EventManager.onWindowResize(this.resize, this); 
30278
30279         if(!this.isAutoInitial){
30280             this.layout();
30281             return;
30282         }
30283         
30284         this.layout.defer(500,this);
30285     },
30286     
30287     reloadItems: function()
30288     {
30289         this.bricks = this.el.select('.masonry-brick', true);
30290         
30291         this.bricks.each(function(b) {
30292             //Roo.log(b.getSize());
30293             if (!b.attr('originalwidth')) {
30294                 b.attr('originalwidth',  b.getSize().width);
30295             }
30296             
30297         });
30298         
30299         Roo.log(this.bricks.elements.length);
30300     },
30301     
30302     resize : function()
30303     {
30304         Roo.log('resize');
30305         var cs = this.el.getBox(true);
30306         
30307         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30308             Roo.log("no change in with or X");
30309             return;
30310         }
30311         this.currentSize = cs;
30312         this.layout();
30313     },
30314     
30315     layout : function()
30316     {
30317          Roo.log('layout');
30318         this._resetLayout();
30319         //this._manageStamps();
30320       
30321         // don't animate first layout
30322         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30323         this.layoutItems( isInstant );
30324       
30325         // flag for initalized
30326         this._isLayoutInited = true;
30327     },
30328     
30329     layoutItems : function( isInstant )
30330     {
30331         //var items = this._getItemsForLayout( this.items );
30332         // original code supports filtering layout items.. we just ignore it..
30333         
30334         this._layoutItems( this.bricks , isInstant );
30335       
30336         this._postLayout();
30337     },
30338     _layoutItems : function ( items , isInstant)
30339     {
30340        //this.fireEvent( 'layout', this, items );
30341     
30342
30343         if ( !items || !items.elements.length ) {
30344           // no items, emit event with empty array
30345             return;
30346         }
30347
30348         var queue = [];
30349         items.each(function(item) {
30350             Roo.log("layout item");
30351             Roo.log(item);
30352             // get x/y object from method
30353             var position = this._getItemLayoutPosition( item );
30354             // enqueue
30355             position.item = item;
30356             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30357             queue.push( position );
30358         }, this);
30359       
30360         this._processLayoutQueue( queue );
30361     },
30362     /** Sets position of item in DOM
30363     * @param {Element} item
30364     * @param {Number} x - horizontal position
30365     * @param {Number} y - vertical position
30366     * @param {Boolean} isInstant - disables transitions
30367     */
30368     _processLayoutQueue : function( queue )
30369     {
30370         for ( var i=0, len = queue.length; i < len; i++ ) {
30371             var obj = queue[i];
30372             obj.item.position('absolute');
30373             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30374         }
30375     },
30376       
30377     
30378     /**
30379     * Any logic you want to do after each layout,
30380     * i.e. size the container
30381     */
30382     _postLayout : function()
30383     {
30384         this.resizeContainer();
30385     },
30386     
30387     resizeContainer : function()
30388     {
30389         if ( !this.isResizingContainer ) {
30390             return;
30391         }
30392         var size = this._getContainerSize();
30393         if ( size ) {
30394             this.el.setSize(size.width,size.height);
30395             this.boxesEl.setSize(size.width,size.height);
30396         }
30397     },
30398     
30399     
30400     
30401     _resetLayout : function()
30402     {
30403         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30404         this.colWidth = this.el.getWidth();
30405         //this.gutter = this.el.getWidth(); 
30406         
30407         this.measureColumns();
30408
30409         // reset column Y
30410         var i = this.cols;
30411         this.colYs = [];
30412         while (i--) {
30413             this.colYs.push( 0 );
30414         }
30415     
30416         this.maxY = 0;
30417     },
30418
30419     measureColumns : function()
30420     {
30421         this.getContainerWidth();
30422       // if columnWidth is 0, default to outerWidth of first item
30423         if ( !this.columnWidth ) {
30424             var firstItem = this.bricks.first();
30425             Roo.log(firstItem);
30426             this.columnWidth  = this.containerWidth;
30427             if (firstItem && firstItem.attr('originalwidth') ) {
30428                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30429             }
30430             // columnWidth fall back to item of first element
30431             Roo.log("set column width?");
30432                         this.initialColumnWidth = this.columnWidth  ;
30433
30434             // if first elem has no width, default to size of container
30435             
30436         }
30437         
30438         
30439         if (this.initialColumnWidth) {
30440             this.columnWidth = this.initialColumnWidth;
30441         }
30442         
30443         
30444             
30445         // column width is fixed at the top - however if container width get's smaller we should
30446         // reduce it...
30447         
30448         // this bit calcs how man columns..
30449             
30450         var columnWidth = this.columnWidth += this.gutter;
30451       
30452         // calculate columns
30453         var containerWidth = this.containerWidth + this.gutter;
30454         
30455         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30456         // fix rounding errors, typically with gutters
30457         var excess = columnWidth - containerWidth % columnWidth;
30458         
30459         
30460         // if overshoot is less than a pixel, round up, otherwise floor it
30461         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30462         cols = Math[ mathMethod ]( cols );
30463         this.cols = Math.max( cols, 1 );
30464         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30465         
30466          // padding positioning..
30467         var totalColWidth = this.cols * this.columnWidth;
30468         var padavail = this.containerWidth - totalColWidth;
30469         // so for 2 columns - we need 3 'pads'
30470         
30471         var padNeeded = (1+this.cols) * this.padWidth;
30472         
30473         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30474         
30475         this.columnWidth += padExtra
30476         //this.padWidth = Math.floor(padavail /  ( this.cols));
30477         
30478         // adjust colum width so that padding is fixed??
30479         
30480         // we have 3 columns ... total = width * 3
30481         // we have X left over... that should be used by 
30482         
30483         //if (this.expandC) {
30484             
30485         //}
30486         
30487         
30488         
30489     },
30490     
30491     getContainerWidth : function()
30492     {
30493        /* // container is parent if fit width
30494         var container = this.isFitWidth ? this.element.parentNode : this.element;
30495         // check that this.size and size are there
30496         // IE8 triggers resize on body size change, so they might not be
30497         
30498         var size = getSize( container );  //FIXME
30499         this.containerWidth = size && size.innerWidth; //FIXME
30500         */
30501          
30502         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30503         
30504     },
30505     
30506     _getItemLayoutPosition : function( item )  // what is item?
30507     {
30508         // we resize the item to our columnWidth..
30509       
30510         item.setWidth(this.columnWidth);
30511         item.autoBoxAdjust  = false;
30512         
30513         var sz = item.getSize();
30514  
30515         // how many columns does this brick span
30516         var remainder = this.containerWidth % this.columnWidth;
30517         
30518         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30519         // round if off by 1 pixel, otherwise use ceil
30520         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30521         colSpan = Math.min( colSpan, this.cols );
30522         
30523         // normally this should be '1' as we dont' currently allow multi width columns..
30524         
30525         var colGroup = this._getColGroup( colSpan );
30526         // get the minimum Y value from the columns
30527         var minimumY = Math.min.apply( Math, colGroup );
30528         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30529         
30530         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30531          
30532         // position the brick
30533         var position = {
30534             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30535             y: this.currentSize.y + minimumY + this.padHeight
30536         };
30537         
30538         Roo.log(position);
30539         // apply setHeight to necessary columns
30540         var setHeight = minimumY + sz.height + this.padHeight;
30541         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30542         
30543         var setSpan = this.cols + 1 - colGroup.length;
30544         for ( var i = 0; i < setSpan; i++ ) {
30545           this.colYs[ shortColIndex + i ] = setHeight ;
30546         }
30547       
30548         return position;
30549     },
30550     
30551     /**
30552      * @param {Number} colSpan - number of columns the element spans
30553      * @returns {Array} colGroup
30554      */
30555     _getColGroup : function( colSpan )
30556     {
30557         if ( colSpan < 2 ) {
30558           // if brick spans only one column, use all the column Ys
30559           return this.colYs;
30560         }
30561       
30562         var colGroup = [];
30563         // how many different places could this brick fit horizontally
30564         var groupCount = this.cols + 1 - colSpan;
30565         // for each group potential horizontal position
30566         for ( var i = 0; i < groupCount; i++ ) {
30567           // make an array of colY values for that one group
30568           var groupColYs = this.colYs.slice( i, i + colSpan );
30569           // and get the max value of the array
30570           colGroup[i] = Math.max.apply( Math, groupColYs );
30571         }
30572         return colGroup;
30573     },
30574     /*
30575     _manageStamp : function( stamp )
30576     {
30577         var stampSize =  stamp.getSize();
30578         var offset = stamp.getBox();
30579         // get the columns that this stamp affects
30580         var firstX = this.isOriginLeft ? offset.x : offset.right;
30581         var lastX = firstX + stampSize.width;
30582         var firstCol = Math.floor( firstX / this.columnWidth );
30583         firstCol = Math.max( 0, firstCol );
30584         
30585         var lastCol = Math.floor( lastX / this.columnWidth );
30586         // lastCol should not go over if multiple of columnWidth #425
30587         lastCol -= lastX % this.columnWidth ? 0 : 1;
30588         lastCol = Math.min( this.cols - 1, lastCol );
30589         
30590         // set colYs to bottom of the stamp
30591         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30592             stampSize.height;
30593             
30594         for ( var i = firstCol; i <= lastCol; i++ ) {
30595           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30596         }
30597     },
30598     */
30599     
30600     _getContainerSize : function()
30601     {
30602         this.maxY = Math.max.apply( Math, this.colYs );
30603         var size = {
30604             height: this.maxY
30605         };
30606       
30607         if ( this.isFitWidth ) {
30608             size.width = this._getContainerFitWidth();
30609         }
30610       
30611         return size;
30612     },
30613     
30614     _getContainerFitWidth : function()
30615     {
30616         var unusedCols = 0;
30617         // count unused columns
30618         var i = this.cols;
30619         while ( --i ) {
30620           if ( this.colYs[i] !== 0 ) {
30621             break;
30622           }
30623           unusedCols++;
30624         }
30625         // fit container to columns that have been used
30626         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30627     },
30628     
30629     needsResizeLayout : function()
30630     {
30631         var previousWidth = this.containerWidth;
30632         this.getContainerWidth();
30633         return previousWidth !== this.containerWidth;
30634     }
30635  
30636 });
30637
30638  
30639
30640  /*
30641  * - LGPL
30642  *
30643  * element
30644  * 
30645  */
30646
30647 /**
30648  * @class Roo.bootstrap.MasonryBrick
30649  * @extends Roo.bootstrap.Component
30650  * Bootstrap MasonryBrick class
30651  * 
30652  * @constructor
30653  * Create a new MasonryBrick
30654  * @param {Object} config The config object
30655  */
30656
30657 Roo.bootstrap.MasonryBrick = function(config){
30658     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30659     
30660     this.addEvents({
30661         // raw events
30662         /**
30663          * @event click
30664          * When a MasonryBrick is clcik
30665          * @param {Roo.bootstrap.MasonryBrick} this
30666          * @param {Roo.EventObject} e
30667          */
30668         "click" : true
30669     });
30670 };
30671
30672 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30673     
30674     /**
30675      * @cfg {String} title
30676      */   
30677     title : '',
30678     /**
30679      * @cfg {String} html
30680      */   
30681     html : '',
30682     /**
30683      * @cfg {String} bgimage
30684      */   
30685     bgimage : '',
30686     /**
30687      * @cfg {String} videourl
30688      */   
30689     videourl : '',
30690     /**
30691      * @cfg {String} cls
30692      */   
30693     cls : '',
30694     /**
30695      * @cfg {String} href
30696      */   
30697     href : '',
30698     /**
30699      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30700      */   
30701     size : 'xs',
30702     
30703     /**
30704      * @cfg {String} (center|bottom) placetitle
30705      */   
30706     placetitle : '',
30707     
30708     getAutoCreate : function()
30709     {
30710         var cls = 'masonry-brick';
30711         
30712         if(this.href.length){
30713             cls += ' masonry-brick-link';
30714         }
30715         
30716         if(this.bgimage.length){
30717             cls += ' masonry-brick-image';
30718         }
30719         
30720         if(this.size){
30721             cls += ' masonry-' + this.size + '-brick';
30722         }
30723         
30724         if(this.placetitle.length){
30725             
30726             switch (this.placetitle) {
30727                 case 'center' :
30728                     cls += ' masonry-center-title';
30729                     break;
30730                 case 'bottom' :
30731                     cls += ' masonry-bottom-title';
30732                     break;
30733                 default:
30734                     break;
30735             }
30736             
30737         } else {
30738             if(!this.html.length && !this.bgimage.length){
30739                 cls += ' masonry-center-title';
30740             }
30741
30742             if(!this.html.length && this.bgimage.length){
30743                 cls += ' masonry-bottom-title';
30744             }
30745         }
30746         
30747         if(this.cls){
30748             cls += ' ' + this.cls;
30749         }
30750         
30751         var cfg = {
30752             tag: (this.href.length) ? 'a' : 'div',
30753             cls: cls,
30754             cn: [
30755                 {
30756                     tag: 'div',
30757                     cls: 'masonry-brick-paragraph',
30758                     cn: []
30759                 }
30760             ]
30761         };
30762         
30763         if(this.href.length){
30764             cfg.href = this.href;
30765         }
30766         
30767         var cn = cfg.cn[0].cn;
30768         
30769         if(this.title.length){
30770             cn.push({
30771                 tag: 'h4',
30772                 cls: 'masonry-brick-title',
30773                 html: this.title
30774             });
30775         }
30776         
30777         if(this.html.length){
30778             cn.push({
30779                 tag: 'p',
30780                 cls: 'masonry-brick-text',
30781                 html: this.html
30782             });
30783         }  
30784         if (!this.title.length && !this.html.length) {
30785             cfg.cn[0].cls += ' hide';
30786         }
30787         
30788         if(this.bgimage.length){
30789             cfg.cn.push({
30790                 tag: 'img',
30791                 cls: 'masonry-brick-image-view',
30792                 src: this.bgimage
30793             });
30794         }
30795         if(this.videourl.length){
30796             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30797             // youtube support only?
30798             cfg.cn.push({
30799                 tag: 'iframe',
30800                 cls: 'masonry-brick-image-view',
30801                 src: vurl,
30802                 frameborder : 0,
30803                 allowfullscreen : true
30804             });
30805             
30806             
30807         }
30808         return cfg;
30809         
30810     },
30811     
30812     initEvents: function() 
30813     {
30814         switch (this.size) {
30815             case 'xs' :
30816 //                this.intSize = 1;
30817                 this.x = 1;
30818                 this.y = 1;
30819                 break;
30820             case 'sm' :
30821 //                this.intSize = 2;
30822                 this.x = 2;
30823                 this.y = 2;
30824                 break;
30825             case 'md' :
30826             case 'md-left' :
30827             case 'md-right' :
30828 //                this.intSize = 3;
30829                 this.x = 3;
30830                 this.y = 3;
30831                 break;
30832             case 'tall' :
30833 //                this.intSize = 3;
30834                 this.x = 2;
30835                 this.y = 3;
30836                 break;
30837             case 'wide' :
30838 //                this.intSize = 3;
30839                 this.x = 3;
30840                 this.y = 2;
30841                 break;
30842             case 'wide-thin' :
30843 //                this.intSize = 3;
30844                 this.x = 3;
30845                 this.y = 1;
30846                 break;
30847                         
30848             default :
30849                 break;
30850         }
30851         
30852         
30853         
30854         if(Roo.isTouch){
30855             this.el.on('touchstart', this.onTouchStart, this);
30856             this.el.on('touchmove', this.onTouchMove, this);
30857             this.el.on('touchend', this.onTouchEnd, this);
30858             this.el.on('contextmenu', this.onContextMenu, this);
30859         } else {
30860             this.el.on('mouseenter'  ,this.enter, this);
30861             this.el.on('mouseleave', this.leave, this);
30862         }
30863         
30864         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30865             this.parent().bricks.push(this);   
30866         }
30867         
30868     },
30869     
30870     onClick: function(e, el)
30871     {
30872         if(!Roo.isTouch){
30873             return;
30874         }
30875         
30876         var time = this.endTimer - this.startTimer;
30877         
30878         //alert(time);
30879         
30880         if(time < 1000){
30881             return;
30882         }
30883         
30884         e.preventDefault();
30885     },
30886     
30887     enter: function(e, el)
30888     {
30889         e.preventDefault();
30890         
30891         if(this.bgimage.length && this.html.length){
30892             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30893         }
30894     },
30895     
30896     leave: function(e, el)
30897     {
30898         e.preventDefault();
30899         
30900         if(this.bgimage.length && this.html.length){
30901             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30902         }
30903     },
30904     
30905     onTouchStart: function(e, el)
30906     {
30907 //        e.preventDefault();
30908         
30909         this.touchmoved = false;
30910         
30911         if(!this.bgimage.length || !this.html.length){
30912             return;
30913         }
30914         
30915         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30916         
30917         this.timer = new Date().getTime();
30918         
30919     },
30920     
30921     onTouchMove: function(e, el)
30922     {
30923         this.touchmoved = true;
30924     },
30925     
30926     onContextMenu : function(e,el)
30927     {
30928         e.preventDefault();
30929         e.stopPropagation();
30930         return false;
30931     },
30932     
30933     onTouchEnd: function(e, el)
30934     {
30935 //        e.preventDefault();
30936         
30937         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30938         
30939             this.leave(e,el);
30940             
30941             return;
30942         }
30943         
30944         if(!this.bgimage.length || !this.html.length){
30945             
30946             if(this.href.length){
30947                 window.location.href = this.href;
30948             }
30949             
30950             return;
30951         }
30952         
30953         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30954         
30955         window.location.href = this.href;
30956     }
30957     
30958 });
30959
30960  
30961
30962  /*
30963  * - LGPL
30964  *
30965  * element
30966  * 
30967  */
30968
30969 /**
30970  * @class Roo.bootstrap.Brick
30971  * @extends Roo.bootstrap.Component
30972  * Bootstrap Brick class
30973  * 
30974  * @constructor
30975  * Create a new Brick
30976  * @param {Object} config The config object
30977  */
30978
30979 Roo.bootstrap.Brick = function(config){
30980     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30981     
30982     this.addEvents({
30983         // raw events
30984         /**
30985          * @event click
30986          * When a Brick is click
30987          * @param {Roo.bootstrap.Brick} this
30988          * @param {Roo.EventObject} e
30989          */
30990         "click" : true
30991     });
30992 };
30993
30994 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30995     
30996     /**
30997      * @cfg {String} title
30998      */   
30999     title : '',
31000     /**
31001      * @cfg {String} html
31002      */   
31003     html : '',
31004     /**
31005      * @cfg {String} bgimage
31006      */   
31007     bgimage : '',
31008     /**
31009      * @cfg {String} cls
31010      */   
31011     cls : '',
31012     /**
31013      * @cfg {String} href
31014      */   
31015     href : '',
31016     /**
31017      * @cfg {String} video
31018      */   
31019     video : '',
31020     /**
31021      * @cfg {Boolean} square
31022      */   
31023     square : true,
31024     
31025     getAutoCreate : function()
31026     {
31027         var cls = 'roo-brick';
31028         
31029         if(this.href.length){
31030             cls += ' roo-brick-link';
31031         }
31032         
31033         if(this.bgimage.length){
31034             cls += ' roo-brick-image';
31035         }
31036         
31037         if(!this.html.length && !this.bgimage.length){
31038             cls += ' roo-brick-center-title';
31039         }
31040         
31041         if(!this.html.length && this.bgimage.length){
31042             cls += ' roo-brick-bottom-title';
31043         }
31044         
31045         if(this.cls){
31046             cls += ' ' + this.cls;
31047         }
31048         
31049         var cfg = {
31050             tag: (this.href.length) ? 'a' : 'div',
31051             cls: cls,
31052             cn: [
31053                 {
31054                     tag: 'div',
31055                     cls: 'roo-brick-paragraph',
31056                     cn: []
31057                 }
31058             ]
31059         };
31060         
31061         if(this.href.length){
31062             cfg.href = this.href;
31063         }
31064         
31065         var cn = cfg.cn[0].cn;
31066         
31067         if(this.title.length){
31068             cn.push({
31069                 tag: 'h4',
31070                 cls: 'roo-brick-title',
31071                 html: this.title
31072             });
31073         }
31074         
31075         if(this.html.length){
31076             cn.push({
31077                 tag: 'p',
31078                 cls: 'roo-brick-text',
31079                 html: this.html
31080             });
31081         } else {
31082             cn.cls += ' hide';
31083         }
31084         
31085         if(this.bgimage.length){
31086             cfg.cn.push({
31087                 tag: 'img',
31088                 cls: 'roo-brick-image-view',
31089                 src: this.bgimage
31090             });
31091         }
31092         
31093         return cfg;
31094     },
31095     
31096     initEvents: function() 
31097     {
31098         if(this.title.length || this.html.length){
31099             this.el.on('mouseenter'  ,this.enter, this);
31100             this.el.on('mouseleave', this.leave, this);
31101         }
31102         
31103         
31104         Roo.EventManager.onWindowResize(this.resize, this); 
31105         
31106         this.resize();
31107     },
31108     
31109     resize : function()
31110     {
31111         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31112         
31113         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31114 //        paragraph.setHeight(paragraph.getWidth());
31115         
31116         if(this.bgimage.length){
31117             var image = this.el.select('.roo-brick-image-view', true).first();
31118             image.setWidth(paragraph.getWidth());
31119             image.setHeight(paragraph.getWidth());
31120         }
31121         
31122     },
31123     
31124     enter: function(e, el)
31125     {
31126         e.preventDefault();
31127         
31128         if(this.bgimage.length){
31129             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31130             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31131         }
31132     },
31133     
31134     leave: function(e, el)
31135     {
31136         e.preventDefault();
31137         
31138         if(this.bgimage.length){
31139             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31140             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31141         }
31142     }
31143     
31144 });
31145
31146  
31147
31148  /*
31149  * Based on:
31150  * Ext JS Library 1.1.1
31151  * Copyright(c) 2006-2007, Ext JS, LLC.
31152  *
31153  * Originally Released Under LGPL - original licence link has changed is not relivant.
31154  *
31155  * Fork - LGPL
31156  * <script type="text/javascript">
31157  */
31158
31159
31160 /**
31161  * @class Roo.bootstrap.SplitBar
31162  * @extends Roo.util.Observable
31163  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31164  * <br><br>
31165  * Usage:
31166  * <pre><code>
31167 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31168                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31169 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31170 split.minSize = 100;
31171 split.maxSize = 600;
31172 split.animate = true;
31173 split.on('moved', splitterMoved);
31174 </code></pre>
31175  * @constructor
31176  * Create a new SplitBar
31177  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31178  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31179  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31180  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31181                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31182                         position of the SplitBar).
31183  */
31184 Roo.bootstrap.SplitBar = function(cfg){
31185     
31186     /** @private */
31187     
31188     //{
31189     //  dragElement : elm
31190     //  resizingElement: el,
31191         // optional..
31192     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31193     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31194         // existingProxy ???
31195     //}
31196     
31197     this.el = Roo.get(cfg.dragElement, true);
31198     this.el.dom.unselectable = "on";
31199     /** @private */
31200     this.resizingEl = Roo.get(cfg.resizingElement, true);
31201
31202     /**
31203      * @private
31204      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31205      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31206      * @type Number
31207      */
31208     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31209     
31210     /**
31211      * The minimum size of the resizing element. (Defaults to 0)
31212      * @type Number
31213      */
31214     this.minSize = 0;
31215     
31216     /**
31217      * The maximum size of the resizing element. (Defaults to 2000)
31218      * @type Number
31219      */
31220     this.maxSize = 2000;
31221     
31222     /**
31223      * Whether to animate the transition to the new size
31224      * @type Boolean
31225      */
31226     this.animate = false;
31227     
31228     /**
31229      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31230      * @type Boolean
31231      */
31232     this.useShim = false;
31233     
31234     /** @private */
31235     this.shim = null;
31236     
31237     if(!cfg.existingProxy){
31238         /** @private */
31239         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31240     }else{
31241         this.proxy = Roo.get(cfg.existingProxy).dom;
31242     }
31243     /** @private */
31244     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31245     
31246     /** @private */
31247     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31248     
31249     /** @private */
31250     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31251     
31252     /** @private */
31253     this.dragSpecs = {};
31254     
31255     /**
31256      * @private The adapter to use to positon and resize elements
31257      */
31258     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31259     this.adapter.init(this);
31260     
31261     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31262         /** @private */
31263         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31264         this.el.addClass("roo-splitbar-h");
31265     }else{
31266         /** @private */
31267         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31268         this.el.addClass("roo-splitbar-v");
31269     }
31270     
31271     this.addEvents({
31272         /**
31273          * @event resize
31274          * Fires when the splitter is moved (alias for {@link #event-moved})
31275          * @param {Roo.bootstrap.SplitBar} this
31276          * @param {Number} newSize the new width or height
31277          */
31278         "resize" : true,
31279         /**
31280          * @event moved
31281          * Fires when the splitter is moved
31282          * @param {Roo.bootstrap.SplitBar} this
31283          * @param {Number} newSize the new width or height
31284          */
31285         "moved" : true,
31286         /**
31287          * @event beforeresize
31288          * Fires before the splitter is dragged
31289          * @param {Roo.bootstrap.SplitBar} this
31290          */
31291         "beforeresize" : true,
31292
31293         "beforeapply" : true
31294     });
31295
31296     Roo.util.Observable.call(this);
31297 };
31298
31299 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31300     onStartProxyDrag : function(x, y){
31301         this.fireEvent("beforeresize", this);
31302         if(!this.overlay){
31303             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31304             o.unselectable();
31305             o.enableDisplayMode("block");
31306             // all splitbars share the same overlay
31307             Roo.bootstrap.SplitBar.prototype.overlay = o;
31308         }
31309         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31310         this.overlay.show();
31311         Roo.get(this.proxy).setDisplayed("block");
31312         var size = this.adapter.getElementSize(this);
31313         this.activeMinSize = this.getMinimumSize();;
31314         this.activeMaxSize = this.getMaximumSize();;
31315         var c1 = size - this.activeMinSize;
31316         var c2 = Math.max(this.activeMaxSize - size, 0);
31317         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31318             this.dd.resetConstraints();
31319             this.dd.setXConstraint(
31320                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31321                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31322             );
31323             this.dd.setYConstraint(0, 0);
31324         }else{
31325             this.dd.resetConstraints();
31326             this.dd.setXConstraint(0, 0);
31327             this.dd.setYConstraint(
31328                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31329                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31330             );
31331          }
31332         this.dragSpecs.startSize = size;
31333         this.dragSpecs.startPoint = [x, y];
31334         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31335     },
31336     
31337     /** 
31338      * @private Called after the drag operation by the DDProxy
31339      */
31340     onEndProxyDrag : function(e){
31341         Roo.get(this.proxy).setDisplayed(false);
31342         var endPoint = Roo.lib.Event.getXY(e);
31343         if(this.overlay){
31344             this.overlay.hide();
31345         }
31346         var newSize;
31347         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31348             newSize = this.dragSpecs.startSize + 
31349                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31350                     endPoint[0] - this.dragSpecs.startPoint[0] :
31351                     this.dragSpecs.startPoint[0] - endPoint[0]
31352                 );
31353         }else{
31354             newSize = this.dragSpecs.startSize + 
31355                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31356                     endPoint[1] - this.dragSpecs.startPoint[1] :
31357                     this.dragSpecs.startPoint[1] - endPoint[1]
31358                 );
31359         }
31360         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31361         if(newSize != this.dragSpecs.startSize){
31362             if(this.fireEvent('beforeapply', this, newSize) !== false){
31363                 this.adapter.setElementSize(this, newSize);
31364                 this.fireEvent("moved", this, newSize);
31365                 this.fireEvent("resize", this, newSize);
31366             }
31367         }
31368     },
31369     
31370     /**
31371      * Get the adapter this SplitBar uses
31372      * @return The adapter object
31373      */
31374     getAdapter : function(){
31375         return this.adapter;
31376     },
31377     
31378     /**
31379      * Set the adapter this SplitBar uses
31380      * @param {Object} adapter A SplitBar adapter object
31381      */
31382     setAdapter : function(adapter){
31383         this.adapter = adapter;
31384         this.adapter.init(this);
31385     },
31386     
31387     /**
31388      * Gets the minimum size for the resizing element
31389      * @return {Number} The minimum size
31390      */
31391     getMinimumSize : function(){
31392         return this.minSize;
31393     },
31394     
31395     /**
31396      * Sets the minimum size for the resizing element
31397      * @param {Number} minSize The minimum size
31398      */
31399     setMinimumSize : function(minSize){
31400         this.minSize = minSize;
31401     },
31402     
31403     /**
31404      * Gets the maximum size for the resizing element
31405      * @return {Number} The maximum size
31406      */
31407     getMaximumSize : function(){
31408         return this.maxSize;
31409     },
31410     
31411     /**
31412      * Sets the maximum size for the resizing element
31413      * @param {Number} maxSize The maximum size
31414      */
31415     setMaximumSize : function(maxSize){
31416         this.maxSize = maxSize;
31417     },
31418     
31419     /**
31420      * Sets the initialize size for the resizing element
31421      * @param {Number} size The initial size
31422      */
31423     setCurrentSize : function(size){
31424         var oldAnimate = this.animate;
31425         this.animate = false;
31426         this.adapter.setElementSize(this, size);
31427         this.animate = oldAnimate;
31428     },
31429     
31430     /**
31431      * Destroy this splitbar. 
31432      * @param {Boolean} removeEl True to remove the element
31433      */
31434     destroy : function(removeEl){
31435         if(this.shim){
31436             this.shim.remove();
31437         }
31438         this.dd.unreg();
31439         this.proxy.parentNode.removeChild(this.proxy);
31440         if(removeEl){
31441             this.el.remove();
31442         }
31443     }
31444 });
31445
31446 /**
31447  * @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.
31448  */
31449 Roo.bootstrap.SplitBar.createProxy = function(dir){
31450     var proxy = new Roo.Element(document.createElement("div"));
31451     proxy.unselectable();
31452     var cls = 'roo-splitbar-proxy';
31453     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31454     document.body.appendChild(proxy.dom);
31455     return proxy.dom;
31456 };
31457
31458 /** 
31459  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31460  * Default Adapter. It assumes the splitter and resizing element are not positioned
31461  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31462  */
31463 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31464 };
31465
31466 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31467     // do nothing for now
31468     init : function(s){
31469     
31470     },
31471     /**
31472      * Called before drag operations to get the current size of the resizing element. 
31473      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31474      */
31475      getElementSize : function(s){
31476         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31477             return s.resizingEl.getWidth();
31478         }else{
31479             return s.resizingEl.getHeight();
31480         }
31481     },
31482     
31483     /**
31484      * Called after drag operations to set the size of the resizing element.
31485      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31486      * @param {Number} newSize The new size to set
31487      * @param {Function} onComplete A function to be invoked when resizing is complete
31488      */
31489     setElementSize : function(s, newSize, onComplete){
31490         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31491             if(!s.animate){
31492                 s.resizingEl.setWidth(newSize);
31493                 if(onComplete){
31494                     onComplete(s, newSize);
31495                 }
31496             }else{
31497                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31498             }
31499         }else{
31500             
31501             if(!s.animate){
31502                 s.resizingEl.setHeight(newSize);
31503                 if(onComplete){
31504                     onComplete(s, newSize);
31505                 }
31506             }else{
31507                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31508             }
31509         }
31510     }
31511 };
31512
31513 /** 
31514  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31515  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31516  * Adapter that  moves the splitter element to align with the resized sizing element. 
31517  * Used with an absolute positioned SplitBar.
31518  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31519  * document.body, make sure you assign an id to the body element.
31520  */
31521 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31522     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31523     this.container = Roo.get(container);
31524 };
31525
31526 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31527     init : function(s){
31528         this.basic.init(s);
31529     },
31530     
31531     getElementSize : function(s){
31532         return this.basic.getElementSize(s);
31533     },
31534     
31535     setElementSize : function(s, newSize, onComplete){
31536         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31537     },
31538     
31539     moveSplitter : function(s){
31540         var yes = Roo.bootstrap.SplitBar;
31541         switch(s.placement){
31542             case yes.LEFT:
31543                 s.el.setX(s.resizingEl.getRight());
31544                 break;
31545             case yes.RIGHT:
31546                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31547                 break;
31548             case yes.TOP:
31549                 s.el.setY(s.resizingEl.getBottom());
31550                 break;
31551             case yes.BOTTOM:
31552                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31553                 break;
31554         }
31555     }
31556 };
31557
31558 /**
31559  * Orientation constant - Create a vertical SplitBar
31560  * @static
31561  * @type Number
31562  */
31563 Roo.bootstrap.SplitBar.VERTICAL = 1;
31564
31565 /**
31566  * Orientation constant - Create a horizontal SplitBar
31567  * @static
31568  * @type Number
31569  */
31570 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31571
31572 /**
31573  * Placement constant - The resizing element is to the left of the splitter element
31574  * @static
31575  * @type Number
31576  */
31577 Roo.bootstrap.SplitBar.LEFT = 1;
31578
31579 /**
31580  * Placement constant - The resizing element is to the right of the splitter element
31581  * @static
31582  * @type Number
31583  */
31584 Roo.bootstrap.SplitBar.RIGHT = 2;
31585
31586 /**
31587  * Placement constant - The resizing element is positioned above the splitter element
31588  * @static
31589  * @type Number
31590  */
31591 Roo.bootstrap.SplitBar.TOP = 3;
31592
31593 /**
31594  * Placement constant - The resizing element is positioned under splitter element
31595  * @static
31596  * @type Number
31597  */
31598 Roo.bootstrap.SplitBar.BOTTOM = 4;
31599 Roo.namespace("Roo.bootstrap.layout");/*
31600  * Based on:
31601  * Ext JS Library 1.1.1
31602  * Copyright(c) 2006-2007, Ext JS, LLC.
31603  *
31604  * Originally Released Under LGPL - original licence link has changed is not relivant.
31605  *
31606  * Fork - LGPL
31607  * <script type="text/javascript">
31608  */
31609  
31610 /**
31611  * @class Roo.bootstrap.layout.Manager
31612  * @extends Roo.bootstrap.Component
31613  * Base class for layout managers.
31614  */
31615 Roo.bootstrap.layout.Manager = function(config)
31616 {
31617     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31618     
31619     
31620      
31621     
31622     
31623     /** false to disable window resize monitoring @type Boolean */
31624     this.monitorWindowResize = true;
31625     this.regions = {};
31626     this.addEvents({
31627         /**
31628          * @event layout
31629          * Fires when a layout is performed. 
31630          * @param {Roo.LayoutManager} this
31631          */
31632         "layout" : true,
31633         /**
31634          * @event regionresized
31635          * Fires when the user resizes a region. 
31636          * @param {Roo.LayoutRegion} region The resized region
31637          * @param {Number} newSize The new size (width for east/west, height for north/south)
31638          */
31639         "regionresized" : true,
31640         /**
31641          * @event regioncollapsed
31642          * Fires when a region is collapsed. 
31643          * @param {Roo.LayoutRegion} region The collapsed region
31644          */
31645         "regioncollapsed" : true,
31646         /**
31647          * @event regionexpanded
31648          * Fires when a region is expanded.  
31649          * @param {Roo.LayoutRegion} region The expanded region
31650          */
31651         "regionexpanded" : true
31652     });
31653     this.updating = false;
31654     
31655     if (config.el) {
31656         this.el = Roo.get(config.el);
31657         this.initEvents();
31658     }
31659     
31660 };
31661
31662 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31663     
31664     
31665     regions : null,
31666     
31667     monitorWindowResize : true,
31668     
31669     
31670     updating : false,
31671     
31672     
31673     onRender : function(ct, position)
31674     {
31675         if(!this.el){
31676             this.el = Roo.get(ct);
31677             this.initEvents();
31678         }
31679     },
31680     
31681     
31682     initEvents: function()
31683     {
31684         
31685         
31686         // ie scrollbar fix
31687         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31688             document.body.scroll = "no";
31689         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31690             this.el.position('relative');
31691         }
31692         this.id = this.el.id;
31693         this.el.addClass("roo-layout-container");
31694         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31695         if(this.el.dom != document.body ) {
31696             this.el.on('resize', this.layout,this);
31697             this.el.on('show', this.layout,this);
31698         }
31699
31700     },
31701     
31702     /**
31703      * Returns true if this layout is currently being updated
31704      * @return {Boolean}
31705      */
31706     isUpdating : function(){
31707         return this.updating; 
31708     },
31709     
31710     /**
31711      * Suspend the LayoutManager from doing auto-layouts while
31712      * making multiple add or remove calls
31713      */
31714     beginUpdate : function(){
31715         this.updating = true;    
31716     },
31717     
31718     /**
31719      * Restore auto-layouts and optionally disable the manager from performing a layout
31720      * @param {Boolean} noLayout true to disable a layout update 
31721      */
31722     endUpdate : function(noLayout){
31723         this.updating = false;
31724         if(!noLayout){
31725             this.layout();
31726         }    
31727     },
31728     
31729     layout: function(){
31730         // abstract...
31731     },
31732     
31733     onRegionResized : function(region, newSize){
31734         this.fireEvent("regionresized", region, newSize);
31735         this.layout();
31736     },
31737     
31738     onRegionCollapsed : function(region){
31739         this.fireEvent("regioncollapsed", region);
31740     },
31741     
31742     onRegionExpanded : function(region){
31743         this.fireEvent("regionexpanded", region);
31744     },
31745         
31746     /**
31747      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31748      * performs box-model adjustments.
31749      * @return {Object} The size as an object {width: (the width), height: (the height)}
31750      */
31751     getViewSize : function()
31752     {
31753         var size;
31754         if(this.el.dom != document.body){
31755             size = this.el.getSize();
31756         }else{
31757             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31758         }
31759         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31760         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31761         return size;
31762     },
31763     
31764     /**
31765      * Returns the Element this layout is bound to.
31766      * @return {Roo.Element}
31767      */
31768     getEl : function(){
31769         return this.el;
31770     },
31771     
31772     /**
31773      * Returns the specified region.
31774      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31775      * @return {Roo.LayoutRegion}
31776      */
31777     getRegion : function(target){
31778         return this.regions[target.toLowerCase()];
31779     },
31780     
31781     onWindowResize : function(){
31782         if(this.monitorWindowResize){
31783             this.layout();
31784         }
31785     }
31786 });/*
31787  * Based on:
31788  * Ext JS Library 1.1.1
31789  * Copyright(c) 2006-2007, Ext JS, LLC.
31790  *
31791  * Originally Released Under LGPL - original licence link has changed is not relivant.
31792  *
31793  * Fork - LGPL
31794  * <script type="text/javascript">
31795  */
31796 /**
31797  * @class Roo.bootstrap.layout.Border
31798  * @extends Roo.bootstrap.layout.Manager
31799  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31800  * please see: examples/bootstrap/nested.html<br><br>
31801  
31802 <b>The container the layout is rendered into can be either the body element or any other element.
31803 If it is not the body element, the container needs to either be an absolute positioned element,
31804 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31805 the container size if it is not the body element.</b>
31806
31807 * @constructor
31808 * Create a new Border
31809 * @param {Object} config Configuration options
31810  */
31811 Roo.bootstrap.layout.Border = function(config){
31812     config = config || {};
31813     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31814     
31815     
31816     
31817     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31818         if(config[region]){
31819             config[region].region = region;
31820             this.addRegion(config[region]);
31821         }
31822     },this);
31823     
31824 };
31825
31826 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31827
31828 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31829     /**
31830      * Creates and adds a new region if it doesn't already exist.
31831      * @param {String} target The target region key (north, south, east, west or center).
31832      * @param {Object} config The regions config object
31833      * @return {BorderLayoutRegion} The new region
31834      */
31835     addRegion : function(config)
31836     {
31837         if(!this.regions[config.region]){
31838             var r = this.factory(config);
31839             this.bindRegion(r);
31840         }
31841         return this.regions[config.region];
31842     },
31843
31844     // private (kinda)
31845     bindRegion : function(r){
31846         this.regions[r.config.region] = r;
31847         
31848         r.on("visibilitychange",    this.layout, this);
31849         r.on("paneladded",          this.layout, this);
31850         r.on("panelremoved",        this.layout, this);
31851         r.on("invalidated",         this.layout, this);
31852         r.on("resized",             this.onRegionResized, this);
31853         r.on("collapsed",           this.onRegionCollapsed, this);
31854         r.on("expanded",            this.onRegionExpanded, this);
31855     },
31856
31857     /**
31858      * Performs a layout update.
31859      */
31860     layout : function()
31861     {
31862         if(this.updating) {
31863             return;
31864         }
31865         
31866         // render all the rebions if they have not been done alreayd?
31867         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31868             if(this.regions[region] && !this.regions[region].bodyEl){
31869                 this.regions[region].onRender(this.el)
31870             }
31871         },this);
31872         
31873         var size = this.getViewSize();
31874         var w = size.width;
31875         var h = size.height;
31876         var centerW = w;
31877         var centerH = h;
31878         var centerY = 0;
31879         var centerX = 0;
31880         //var x = 0, y = 0;
31881
31882         var rs = this.regions;
31883         var north = rs["north"];
31884         var south = rs["south"]; 
31885         var west = rs["west"];
31886         var east = rs["east"];
31887         var center = rs["center"];
31888         //if(this.hideOnLayout){ // not supported anymore
31889             //c.el.setStyle("display", "none");
31890         //}
31891         if(north && north.isVisible()){
31892             var b = north.getBox();
31893             var m = north.getMargins();
31894             b.width = w - (m.left+m.right);
31895             b.x = m.left;
31896             b.y = m.top;
31897             centerY = b.height + b.y + m.bottom;
31898             centerH -= centerY;
31899             north.updateBox(this.safeBox(b));
31900         }
31901         if(south && south.isVisible()){
31902             var b = south.getBox();
31903             var m = south.getMargins();
31904             b.width = w - (m.left+m.right);
31905             b.x = m.left;
31906             var totalHeight = (b.height + m.top + m.bottom);
31907             b.y = h - totalHeight + m.top;
31908             centerH -= totalHeight;
31909             south.updateBox(this.safeBox(b));
31910         }
31911         if(west && west.isVisible()){
31912             var b = west.getBox();
31913             var m = west.getMargins();
31914             b.height = centerH - (m.top+m.bottom);
31915             b.x = m.left;
31916             b.y = centerY + m.top;
31917             var totalWidth = (b.width + m.left + m.right);
31918             centerX += totalWidth;
31919             centerW -= totalWidth;
31920             west.updateBox(this.safeBox(b));
31921         }
31922         if(east && east.isVisible()){
31923             var b = east.getBox();
31924             var m = east.getMargins();
31925             b.height = centerH - (m.top+m.bottom);
31926             var totalWidth = (b.width + m.left + m.right);
31927             b.x = w - totalWidth + m.left;
31928             b.y = centerY + m.top;
31929             centerW -= totalWidth;
31930             east.updateBox(this.safeBox(b));
31931         }
31932         if(center){
31933             var m = center.getMargins();
31934             var centerBox = {
31935                 x: centerX + m.left,
31936                 y: centerY + m.top,
31937                 width: centerW - (m.left+m.right),
31938                 height: centerH - (m.top+m.bottom)
31939             };
31940             //if(this.hideOnLayout){
31941                 //center.el.setStyle("display", "block");
31942             //}
31943             center.updateBox(this.safeBox(centerBox));
31944         }
31945         this.el.repaint();
31946         this.fireEvent("layout", this);
31947     },
31948
31949     // private
31950     safeBox : function(box){
31951         box.width = Math.max(0, box.width);
31952         box.height = Math.max(0, box.height);
31953         return box;
31954     },
31955
31956     /**
31957      * Adds a ContentPanel (or subclass) to this layout.
31958      * @param {String} target The target region key (north, south, east, west or center).
31959      * @param {Roo.ContentPanel} panel The panel to add
31960      * @return {Roo.ContentPanel} The added panel
31961      */
31962     add : function(target, panel){
31963          
31964         target = target.toLowerCase();
31965         return this.regions[target].add(panel);
31966     },
31967
31968     /**
31969      * Remove a ContentPanel (or subclass) to this layout.
31970      * @param {String} target The target region key (north, south, east, west or center).
31971      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31972      * @return {Roo.ContentPanel} The removed panel
31973      */
31974     remove : function(target, panel){
31975         target = target.toLowerCase();
31976         return this.regions[target].remove(panel);
31977     },
31978
31979     /**
31980      * Searches all regions for a panel with the specified id
31981      * @param {String} panelId
31982      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31983      */
31984     findPanel : function(panelId){
31985         var rs = this.regions;
31986         for(var target in rs){
31987             if(typeof rs[target] != "function"){
31988                 var p = rs[target].getPanel(panelId);
31989                 if(p){
31990                     return p;
31991                 }
31992             }
31993         }
31994         return null;
31995     },
31996
31997     /**
31998      * Searches all regions for a panel with the specified id and activates (shows) it.
31999      * @param {String/ContentPanel} panelId The panels id or the panel itself
32000      * @return {Roo.ContentPanel} The shown panel or null
32001      */
32002     showPanel : function(panelId) {
32003       var rs = this.regions;
32004       for(var target in rs){
32005          var r = rs[target];
32006          if(typeof r != "function"){
32007             if(r.hasPanel(panelId)){
32008                return r.showPanel(panelId);
32009             }
32010          }
32011       }
32012       return null;
32013    },
32014
32015    /**
32016      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32017      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32018      */
32019    /*
32020     restoreState : function(provider){
32021         if(!provider){
32022             provider = Roo.state.Manager;
32023         }
32024         var sm = new Roo.LayoutStateManager();
32025         sm.init(this, provider);
32026     },
32027 */
32028  
32029  
32030     /**
32031      * Adds a xtype elements to the layout.
32032      * <pre><code>
32033
32034 layout.addxtype({
32035        xtype : 'ContentPanel',
32036        region: 'west',
32037        items: [ .... ]
32038    }
32039 );
32040
32041 layout.addxtype({
32042         xtype : 'NestedLayoutPanel',
32043         region: 'west',
32044         layout: {
32045            center: { },
32046            west: { }   
32047         },
32048         items : [ ... list of content panels or nested layout panels.. ]
32049    }
32050 );
32051 </code></pre>
32052      * @param {Object} cfg Xtype definition of item to add.
32053      */
32054     addxtype : function(cfg)
32055     {
32056         // basically accepts a pannel...
32057         // can accept a layout region..!?!?
32058         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32059         
32060         
32061         // theory?  children can only be panels??
32062         
32063         //if (!cfg.xtype.match(/Panel$/)) {
32064         //    return false;
32065         //}
32066         var ret = false;
32067         
32068         if (typeof(cfg.region) == 'undefined') {
32069             Roo.log("Failed to add Panel, region was not set");
32070             Roo.log(cfg);
32071             return false;
32072         }
32073         var region = cfg.region;
32074         delete cfg.region;
32075         
32076           
32077         var xitems = [];
32078         if (cfg.items) {
32079             xitems = cfg.items;
32080             delete cfg.items;
32081         }
32082         var nb = false;
32083         
32084         switch(cfg.xtype) 
32085         {
32086             case 'Content':  // ContentPanel (el, cfg)
32087             case 'Scroll':  // ContentPanel (el, cfg)
32088             case 'View': 
32089                 cfg.autoCreate = true;
32090                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32091                 //} else {
32092                 //    var el = this.el.createChild();
32093                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32094                 //}
32095                 
32096                 this.add(region, ret);
32097                 break;
32098             
32099             /*
32100             case 'TreePanel': // our new panel!
32101                 cfg.el = this.el.createChild();
32102                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32103                 this.add(region, ret);
32104                 break;
32105             */
32106             
32107             case 'Nest': 
32108                 // create a new Layout (which is  a Border Layout...
32109                 
32110                 var clayout = cfg.layout;
32111                 clayout.el  = this.el.createChild();
32112                 clayout.items   = clayout.items  || [];
32113                 
32114                 delete cfg.layout;
32115                 
32116                 // replace this exitems with the clayout ones..
32117                 xitems = clayout.items;
32118                  
32119                 // force background off if it's in center...
32120                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32121                     cfg.background = false;
32122                 }
32123                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32124                 
32125                 
32126                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32127                 //console.log('adding nested layout panel '  + cfg.toSource());
32128                 this.add(region, ret);
32129                 nb = {}; /// find first...
32130                 break;
32131             
32132             case 'Grid':
32133                 
32134                 // needs grid and region
32135                 
32136                 //var el = this.getRegion(region).el.createChild();
32137                 /*
32138                  *var el = this.el.createChild();
32139                 // create the grid first...
32140                 cfg.grid.container = el;
32141                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32142                 */
32143                 
32144                 if (region == 'center' && this.active ) {
32145                     cfg.background = false;
32146                 }
32147                 
32148                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32149                 
32150                 this.add(region, ret);
32151                 /*
32152                 if (cfg.background) {
32153                     // render grid on panel activation (if panel background)
32154                     ret.on('activate', function(gp) {
32155                         if (!gp.grid.rendered) {
32156                     //        gp.grid.render(el);
32157                         }
32158                     });
32159                 } else {
32160                   //  cfg.grid.render(el);
32161                 }
32162                 */
32163                 break;
32164            
32165            
32166             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32167                 // it was the old xcomponent building that caused this before.
32168                 // espeically if border is the top element in the tree.
32169                 ret = this;
32170                 break; 
32171                 
32172                     
32173                 
32174                 
32175                 
32176             default:
32177                 /*
32178                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32179                     
32180                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32181                     this.add(region, ret);
32182                 } else {
32183                 */
32184                     Roo.log(cfg);
32185                     throw "Can not add '" + cfg.xtype + "' to Border";
32186                     return null;
32187              
32188                                 
32189              
32190         }
32191         this.beginUpdate();
32192         // add children..
32193         var region = '';
32194         var abn = {};
32195         Roo.each(xitems, function(i)  {
32196             region = nb && i.region ? i.region : false;
32197             
32198             var add = ret.addxtype(i);
32199            
32200             if (region) {
32201                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32202                 if (!i.background) {
32203                     abn[region] = nb[region] ;
32204                 }
32205             }
32206             
32207         });
32208         this.endUpdate();
32209
32210         // make the last non-background panel active..
32211         //if (nb) { Roo.log(abn); }
32212         if (nb) {
32213             
32214             for(var r in abn) {
32215                 region = this.getRegion(r);
32216                 if (region) {
32217                     // tried using nb[r], but it does not work..
32218                      
32219                     region.showPanel(abn[r]);
32220                    
32221                 }
32222             }
32223         }
32224         return ret;
32225         
32226     },
32227     
32228     
32229 // private
32230     factory : function(cfg)
32231     {
32232         
32233         var validRegions = Roo.bootstrap.layout.Border.regions;
32234
32235         var target = cfg.region;
32236         cfg.mgr = this;
32237         
32238         var r = Roo.bootstrap.layout;
32239         Roo.log(target);
32240         switch(target){
32241             case "north":
32242                 return new r.North(cfg);
32243             case "south":
32244                 return new r.South(cfg);
32245             case "east":
32246                 return new r.East(cfg);
32247             case "west":
32248                 return new r.West(cfg);
32249             case "center":
32250                 return new r.Center(cfg);
32251         }
32252         throw 'Layout region "'+target+'" not supported.';
32253     }
32254     
32255     
32256 });
32257  /*
32258  * Based on:
32259  * Ext JS Library 1.1.1
32260  * Copyright(c) 2006-2007, Ext JS, LLC.
32261  *
32262  * Originally Released Under LGPL - original licence link has changed is not relivant.
32263  *
32264  * Fork - LGPL
32265  * <script type="text/javascript">
32266  */
32267  
32268 /**
32269  * @class Roo.bootstrap.layout.Basic
32270  * @extends Roo.util.Observable
32271  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32272  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32273  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32274  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32275  * @cfg {string}   region  the region that it inhabits..
32276  * @cfg {bool}   skipConfig skip config?
32277  * 
32278
32279  */
32280 Roo.bootstrap.layout.Basic = function(config){
32281     
32282     this.mgr = config.mgr;
32283     
32284     this.position = config.region;
32285     
32286     var skipConfig = config.skipConfig;
32287     
32288     this.events = {
32289         /**
32290          * @scope Roo.BasicLayoutRegion
32291          */
32292         
32293         /**
32294          * @event beforeremove
32295          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32296          * @param {Roo.LayoutRegion} this
32297          * @param {Roo.ContentPanel} panel The panel
32298          * @param {Object} e The cancel event object
32299          */
32300         "beforeremove" : true,
32301         /**
32302          * @event invalidated
32303          * Fires when the layout for this region is changed.
32304          * @param {Roo.LayoutRegion} this
32305          */
32306         "invalidated" : true,
32307         /**
32308          * @event visibilitychange
32309          * Fires when this region is shown or hidden 
32310          * @param {Roo.LayoutRegion} this
32311          * @param {Boolean} visibility true or false
32312          */
32313         "visibilitychange" : true,
32314         /**
32315          * @event paneladded
32316          * Fires when a panel is added. 
32317          * @param {Roo.LayoutRegion} this
32318          * @param {Roo.ContentPanel} panel The panel
32319          */
32320         "paneladded" : true,
32321         /**
32322          * @event panelremoved
32323          * Fires when a panel is removed. 
32324          * @param {Roo.LayoutRegion} this
32325          * @param {Roo.ContentPanel} panel The panel
32326          */
32327         "panelremoved" : true,
32328         /**
32329          * @event beforecollapse
32330          * Fires when this region before collapse.
32331          * @param {Roo.LayoutRegion} this
32332          */
32333         "beforecollapse" : true,
32334         /**
32335          * @event collapsed
32336          * Fires when this region is collapsed.
32337          * @param {Roo.LayoutRegion} this
32338          */
32339         "collapsed" : true,
32340         /**
32341          * @event expanded
32342          * Fires when this region is expanded.
32343          * @param {Roo.LayoutRegion} this
32344          */
32345         "expanded" : true,
32346         /**
32347          * @event slideshow
32348          * Fires when this region is slid into view.
32349          * @param {Roo.LayoutRegion} this
32350          */
32351         "slideshow" : true,
32352         /**
32353          * @event slidehide
32354          * Fires when this region slides out of view. 
32355          * @param {Roo.LayoutRegion} this
32356          */
32357         "slidehide" : true,
32358         /**
32359          * @event panelactivated
32360          * Fires when a panel is activated. 
32361          * @param {Roo.LayoutRegion} this
32362          * @param {Roo.ContentPanel} panel The activated panel
32363          */
32364         "panelactivated" : true,
32365         /**
32366          * @event resized
32367          * Fires when the user resizes this region. 
32368          * @param {Roo.LayoutRegion} this
32369          * @param {Number} newSize The new size (width for east/west, height for north/south)
32370          */
32371         "resized" : true
32372     };
32373     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32374     this.panels = new Roo.util.MixedCollection();
32375     this.panels.getKey = this.getPanelId.createDelegate(this);
32376     this.box = null;
32377     this.activePanel = null;
32378     // ensure listeners are added...
32379     
32380     if (config.listeners || config.events) {
32381         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32382             listeners : config.listeners || {},
32383             events : config.events || {}
32384         });
32385     }
32386     
32387     if(skipConfig !== true){
32388         this.applyConfig(config);
32389     }
32390 };
32391
32392 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32393 {
32394     getPanelId : function(p){
32395         return p.getId();
32396     },
32397     
32398     applyConfig : function(config){
32399         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32400         this.config = config;
32401         
32402     },
32403     
32404     /**
32405      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32406      * the width, for horizontal (north, south) the height.
32407      * @param {Number} newSize The new width or height
32408      */
32409     resizeTo : function(newSize){
32410         var el = this.el ? this.el :
32411                  (this.activePanel ? this.activePanel.getEl() : null);
32412         if(el){
32413             switch(this.position){
32414                 case "east":
32415                 case "west":
32416                     el.setWidth(newSize);
32417                     this.fireEvent("resized", this, newSize);
32418                 break;
32419                 case "north":
32420                 case "south":
32421                     el.setHeight(newSize);
32422                     this.fireEvent("resized", this, newSize);
32423                 break;                
32424             }
32425         }
32426     },
32427     
32428     getBox : function(){
32429         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32430     },
32431     
32432     getMargins : function(){
32433         return this.margins;
32434     },
32435     
32436     updateBox : function(box){
32437         this.box = box;
32438         var el = this.activePanel.getEl();
32439         el.dom.style.left = box.x + "px";
32440         el.dom.style.top = box.y + "px";
32441         this.activePanel.setSize(box.width, box.height);
32442     },
32443     
32444     /**
32445      * Returns the container element for this region.
32446      * @return {Roo.Element}
32447      */
32448     getEl : function(){
32449         return this.activePanel;
32450     },
32451     
32452     /**
32453      * Returns true if this region is currently visible.
32454      * @return {Boolean}
32455      */
32456     isVisible : function(){
32457         return this.activePanel ? true : false;
32458     },
32459     
32460     setActivePanel : function(panel){
32461         panel = this.getPanel(panel);
32462         if(this.activePanel && this.activePanel != panel){
32463             this.activePanel.setActiveState(false);
32464             this.activePanel.getEl().setLeftTop(-10000,-10000);
32465         }
32466         this.activePanel = panel;
32467         panel.setActiveState(true);
32468         if(this.box){
32469             panel.setSize(this.box.width, this.box.height);
32470         }
32471         this.fireEvent("panelactivated", this, panel);
32472         this.fireEvent("invalidated");
32473     },
32474     
32475     /**
32476      * Show the specified panel.
32477      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32478      * @return {Roo.ContentPanel} The shown panel or null
32479      */
32480     showPanel : function(panel){
32481         panel = this.getPanel(panel);
32482         if(panel){
32483             this.setActivePanel(panel);
32484         }
32485         return panel;
32486     },
32487     
32488     /**
32489      * Get the active panel for this region.
32490      * @return {Roo.ContentPanel} The active panel or null
32491      */
32492     getActivePanel : function(){
32493         return this.activePanel;
32494     },
32495     
32496     /**
32497      * Add the passed ContentPanel(s)
32498      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32499      * @return {Roo.ContentPanel} The panel added (if only one was added)
32500      */
32501     add : function(panel){
32502         if(arguments.length > 1){
32503             for(var i = 0, len = arguments.length; i < len; i++) {
32504                 this.add(arguments[i]);
32505             }
32506             return null;
32507         }
32508         if(this.hasPanel(panel)){
32509             this.showPanel(panel);
32510             return panel;
32511         }
32512         var el = panel.getEl();
32513         if(el.dom.parentNode != this.mgr.el.dom){
32514             this.mgr.el.dom.appendChild(el.dom);
32515         }
32516         if(panel.setRegion){
32517             panel.setRegion(this);
32518         }
32519         this.panels.add(panel);
32520         el.setStyle("position", "absolute");
32521         if(!panel.background){
32522             this.setActivePanel(panel);
32523             if(this.config.initialSize && this.panels.getCount()==1){
32524                 this.resizeTo(this.config.initialSize);
32525             }
32526         }
32527         this.fireEvent("paneladded", this, panel);
32528         return panel;
32529     },
32530     
32531     /**
32532      * Returns true if the panel is in this region.
32533      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32534      * @return {Boolean}
32535      */
32536     hasPanel : function(panel){
32537         if(typeof panel == "object"){ // must be panel obj
32538             panel = panel.getId();
32539         }
32540         return this.getPanel(panel) ? true : false;
32541     },
32542     
32543     /**
32544      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32545      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32546      * @param {Boolean} preservePanel Overrides the config preservePanel option
32547      * @return {Roo.ContentPanel} The panel that was removed
32548      */
32549     remove : function(panel, preservePanel){
32550         panel = this.getPanel(panel);
32551         if(!panel){
32552             return null;
32553         }
32554         var e = {};
32555         this.fireEvent("beforeremove", this, panel, e);
32556         if(e.cancel === true){
32557             return null;
32558         }
32559         var panelId = panel.getId();
32560         this.panels.removeKey(panelId);
32561         return panel;
32562     },
32563     
32564     /**
32565      * Returns the panel specified or null if it's not in this region.
32566      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32567      * @return {Roo.ContentPanel}
32568      */
32569     getPanel : function(id){
32570         if(typeof id == "object"){ // must be panel obj
32571             return id;
32572         }
32573         return this.panels.get(id);
32574     },
32575     
32576     /**
32577      * Returns this regions position (north/south/east/west/center).
32578      * @return {String} 
32579      */
32580     getPosition: function(){
32581         return this.position;    
32582     }
32583 });/*
32584  * Based on:
32585  * Ext JS Library 1.1.1
32586  * Copyright(c) 2006-2007, Ext JS, LLC.
32587  *
32588  * Originally Released Under LGPL - original licence link has changed is not relivant.
32589  *
32590  * Fork - LGPL
32591  * <script type="text/javascript">
32592  */
32593  
32594 /**
32595  * @class Roo.bootstrap.layout.Region
32596  * @extends Roo.bootstrap.layout.Basic
32597  * This class represents a region in a layout manager.
32598  
32599  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32600  * @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})
32601  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32602  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32603  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32604  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32605  * @cfg {String}    title           The title for the region (overrides panel titles)
32606  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32607  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32608  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32609  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32610  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32611  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32612  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32613  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32614  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32615  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32616
32617  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32618  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32619  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32620  * @cfg {Number}    width           For East/West panels
32621  * @cfg {Number}    height          For North/South panels
32622  * @cfg {Boolean}   split           To show the splitter
32623  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32624  * 
32625  * @cfg {string}   cls             Extra CSS classes to add to region
32626  * 
32627  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32628  * @cfg {string}   region  the region that it inhabits..
32629  *
32630
32631  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32632  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32633
32634  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32635  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32636  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32637  */
32638 Roo.bootstrap.layout.Region = function(config)
32639 {
32640     this.applyConfig(config);
32641
32642     var mgr = config.mgr;
32643     var pos = config.region;
32644     config.skipConfig = true;
32645     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32646     
32647     if (mgr.el) {
32648         this.onRender(mgr.el);   
32649     }
32650      
32651     this.visible = true;
32652     this.collapsed = false;
32653     this.unrendered_panels = [];
32654 };
32655
32656 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32657
32658     position: '', // set by wrapper (eg. north/south etc..)
32659     unrendered_panels : null,  // unrendered panels.
32660     createBody : function(){
32661         /** This region's body element 
32662         * @type Roo.Element */
32663         this.bodyEl = this.el.createChild({
32664                 tag: "div",
32665                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32666         });
32667     },
32668
32669     onRender: function(ctr, pos)
32670     {
32671         var dh = Roo.DomHelper;
32672         /** This region's container element 
32673         * @type Roo.Element */
32674         this.el = dh.append(ctr.dom, {
32675                 tag: "div",
32676                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32677             }, true);
32678         /** This region's title element 
32679         * @type Roo.Element */
32680     
32681         this.titleEl = dh.append(this.el.dom,
32682             {
32683                     tag: "div",
32684                     unselectable: "on",
32685                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32686                     children:[
32687                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32688                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32689                     ]}, true);
32690         
32691         this.titleEl.enableDisplayMode();
32692         /** This region's title text element 
32693         * @type HTMLElement */
32694         this.titleTextEl = this.titleEl.dom.firstChild;
32695         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32696         /*
32697         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32698         this.closeBtn.enableDisplayMode();
32699         this.closeBtn.on("click", this.closeClicked, this);
32700         this.closeBtn.hide();
32701     */
32702         this.createBody(this.config);
32703         if(this.config.hideWhenEmpty){
32704             this.hide();
32705             this.on("paneladded", this.validateVisibility, this);
32706             this.on("panelremoved", this.validateVisibility, this);
32707         }
32708         if(this.autoScroll){
32709             this.bodyEl.setStyle("overflow", "auto");
32710         }else{
32711             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32712         }
32713         //if(c.titlebar !== false){
32714             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32715                 this.titleEl.hide();
32716             }else{
32717                 this.titleEl.show();
32718                 if(this.config.title){
32719                     this.titleTextEl.innerHTML = this.config.title;
32720                 }
32721             }
32722         //}
32723         if(this.config.collapsed){
32724             this.collapse(true);
32725         }
32726         if(this.config.hidden){
32727             this.hide();
32728         }
32729         
32730         if (this.unrendered_panels && this.unrendered_panels.length) {
32731             for (var i =0;i< this.unrendered_panels.length; i++) {
32732                 this.add(this.unrendered_panels[i]);
32733             }
32734             this.unrendered_panels = null;
32735             
32736         }
32737         
32738     },
32739     
32740     applyConfig : function(c)
32741     {
32742         /*
32743          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32744             var dh = Roo.DomHelper;
32745             if(c.titlebar !== false){
32746                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32747                 this.collapseBtn.on("click", this.collapse, this);
32748                 this.collapseBtn.enableDisplayMode();
32749                 /*
32750                 if(c.showPin === true || this.showPin){
32751                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32752                     this.stickBtn.enableDisplayMode();
32753                     this.stickBtn.on("click", this.expand, this);
32754                     this.stickBtn.hide();
32755                 }
32756                 
32757             }
32758             */
32759             /** This region's collapsed element
32760             * @type Roo.Element */
32761             /*
32762              *
32763             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32764                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32765             ]}, true);
32766             
32767             if(c.floatable !== false){
32768                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32769                this.collapsedEl.on("click", this.collapseClick, this);
32770             }
32771
32772             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32773                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32774                    id: "message", unselectable: "on", style:{"float":"left"}});
32775                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32776              }
32777             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32778             this.expandBtn.on("click", this.expand, this);
32779             
32780         }
32781         
32782         if(this.collapseBtn){
32783             this.collapseBtn.setVisible(c.collapsible == true);
32784         }
32785         
32786         this.cmargins = c.cmargins || this.cmargins ||
32787                          (this.position == "west" || this.position == "east" ?
32788                              {top: 0, left: 2, right:2, bottom: 0} :
32789                              {top: 2, left: 0, right:0, bottom: 2});
32790         */
32791         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32792         
32793         
32794         this.bottomTabs = c.tabPosition != "top";
32795         
32796         this.autoScroll = c.autoScroll || false;
32797         
32798         
32799        
32800         
32801         this.duration = c.duration || .30;
32802         this.slideDuration = c.slideDuration || .45;
32803         this.config = c;
32804        
32805     },
32806     /**
32807      * Returns true if this region is currently visible.
32808      * @return {Boolean}
32809      */
32810     isVisible : function(){
32811         return this.visible;
32812     },
32813
32814     /**
32815      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32816      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32817      */
32818     //setCollapsedTitle : function(title){
32819     //    title = title || "&#160;";
32820      //   if(this.collapsedTitleTextEl){
32821       //      this.collapsedTitleTextEl.innerHTML = title;
32822        // }
32823     //},
32824
32825     getBox : function(){
32826         var b;
32827       //  if(!this.collapsed){
32828             b = this.el.getBox(false, true);
32829        // }else{
32830           //  b = this.collapsedEl.getBox(false, true);
32831         //}
32832         return b;
32833     },
32834
32835     getMargins : function(){
32836         return this.margins;
32837         //return this.collapsed ? this.cmargins : this.margins;
32838     },
32839 /*
32840     highlight : function(){
32841         this.el.addClass("x-layout-panel-dragover");
32842     },
32843
32844     unhighlight : function(){
32845         this.el.removeClass("x-layout-panel-dragover");
32846     },
32847 */
32848     updateBox : function(box)
32849     {
32850         if (!this.bodyEl) {
32851             return; // not rendered yet..
32852         }
32853         
32854         this.box = box;
32855         if(!this.collapsed){
32856             this.el.dom.style.left = box.x + "px";
32857             this.el.dom.style.top = box.y + "px";
32858             this.updateBody(box.width, box.height);
32859         }else{
32860             this.collapsedEl.dom.style.left = box.x + "px";
32861             this.collapsedEl.dom.style.top = box.y + "px";
32862             this.collapsedEl.setSize(box.width, box.height);
32863         }
32864         if(this.tabs){
32865             this.tabs.autoSizeTabs();
32866         }
32867     },
32868
32869     updateBody : function(w, h)
32870     {
32871         if(w !== null){
32872             this.el.setWidth(w);
32873             w -= this.el.getBorderWidth("rl");
32874             if(this.config.adjustments){
32875                 w += this.config.adjustments[0];
32876             }
32877         }
32878         if(h !== null && h > 0){
32879             this.el.setHeight(h);
32880             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32881             h -= this.el.getBorderWidth("tb");
32882             if(this.config.adjustments){
32883                 h += this.config.adjustments[1];
32884             }
32885             this.bodyEl.setHeight(h);
32886             if(this.tabs){
32887                 h = this.tabs.syncHeight(h);
32888             }
32889         }
32890         if(this.panelSize){
32891             w = w !== null ? w : this.panelSize.width;
32892             h = h !== null ? h : this.panelSize.height;
32893         }
32894         if(this.activePanel){
32895             var el = this.activePanel.getEl();
32896             w = w !== null ? w : el.getWidth();
32897             h = h !== null ? h : el.getHeight();
32898             this.panelSize = {width: w, height: h};
32899             this.activePanel.setSize(w, h);
32900         }
32901         if(Roo.isIE && this.tabs){
32902             this.tabs.el.repaint();
32903         }
32904     },
32905
32906     /**
32907      * Returns the container element for this region.
32908      * @return {Roo.Element}
32909      */
32910     getEl : function(){
32911         return this.el;
32912     },
32913
32914     /**
32915      * Hides this region.
32916      */
32917     hide : function(){
32918         //if(!this.collapsed){
32919             this.el.dom.style.left = "-2000px";
32920             this.el.hide();
32921         //}else{
32922          //   this.collapsedEl.dom.style.left = "-2000px";
32923          //   this.collapsedEl.hide();
32924        // }
32925         this.visible = false;
32926         this.fireEvent("visibilitychange", this, false);
32927     },
32928
32929     /**
32930      * Shows this region if it was previously hidden.
32931      */
32932     show : function(){
32933         //if(!this.collapsed){
32934             this.el.show();
32935         //}else{
32936         //    this.collapsedEl.show();
32937        // }
32938         this.visible = true;
32939         this.fireEvent("visibilitychange", this, true);
32940     },
32941 /*
32942     closeClicked : function(){
32943         if(this.activePanel){
32944             this.remove(this.activePanel);
32945         }
32946     },
32947
32948     collapseClick : function(e){
32949         if(this.isSlid){
32950            e.stopPropagation();
32951            this.slideIn();
32952         }else{
32953            e.stopPropagation();
32954            this.slideOut();
32955         }
32956     },
32957 */
32958     /**
32959      * Collapses this region.
32960      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32961      */
32962     /*
32963     collapse : function(skipAnim, skipCheck = false){
32964         if(this.collapsed) {
32965             return;
32966         }
32967         
32968         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32969             
32970             this.collapsed = true;
32971             if(this.split){
32972                 this.split.el.hide();
32973             }
32974             if(this.config.animate && skipAnim !== true){
32975                 this.fireEvent("invalidated", this);
32976                 this.animateCollapse();
32977             }else{
32978                 this.el.setLocation(-20000,-20000);
32979                 this.el.hide();
32980                 this.collapsedEl.show();
32981                 this.fireEvent("collapsed", this);
32982                 this.fireEvent("invalidated", this);
32983             }
32984         }
32985         
32986     },
32987 */
32988     animateCollapse : function(){
32989         // overridden
32990     },
32991
32992     /**
32993      * Expands this region if it was previously collapsed.
32994      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32995      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32996      */
32997     /*
32998     expand : function(e, skipAnim){
32999         if(e) {
33000             e.stopPropagation();
33001         }
33002         if(!this.collapsed || this.el.hasActiveFx()) {
33003             return;
33004         }
33005         if(this.isSlid){
33006             this.afterSlideIn();
33007             skipAnim = true;
33008         }
33009         this.collapsed = false;
33010         if(this.config.animate && skipAnim !== true){
33011             this.animateExpand();
33012         }else{
33013             this.el.show();
33014             if(this.split){
33015                 this.split.el.show();
33016             }
33017             this.collapsedEl.setLocation(-2000,-2000);
33018             this.collapsedEl.hide();
33019             this.fireEvent("invalidated", this);
33020             this.fireEvent("expanded", this);
33021         }
33022     },
33023 */
33024     animateExpand : function(){
33025         // overridden
33026     },
33027
33028     initTabs : function()
33029     {
33030         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33031         
33032         var ts = new Roo.bootstrap.panel.Tabs({
33033                 el: this.bodyEl.dom,
33034                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33035                 disableTooltips: this.config.disableTabTips,
33036                 toolbar : this.config.toolbar
33037             });
33038         
33039         if(this.config.hideTabs){
33040             ts.stripWrap.setDisplayed(false);
33041         }
33042         this.tabs = ts;
33043         ts.resizeTabs = this.config.resizeTabs === true;
33044         ts.minTabWidth = this.config.minTabWidth || 40;
33045         ts.maxTabWidth = this.config.maxTabWidth || 250;
33046         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33047         ts.monitorResize = false;
33048         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33049         ts.bodyEl.addClass('roo-layout-tabs-body');
33050         this.panels.each(this.initPanelAsTab, this);
33051     },
33052
33053     initPanelAsTab : function(panel){
33054         var ti = this.tabs.addTab(
33055                     panel.getEl().id,
33056                     panel.getTitle(),
33057                     null,
33058                     this.config.closeOnTab && panel.isClosable()
33059             );
33060         if(panel.tabTip !== undefined){
33061             ti.setTooltip(panel.tabTip);
33062         }
33063         ti.on("activate", function(){
33064               this.setActivePanel(panel);
33065         }, this);
33066         
33067         if(this.config.closeOnTab){
33068             ti.on("beforeclose", function(t, e){
33069                 e.cancel = true;
33070                 this.remove(panel);
33071             }, this);
33072         }
33073         return ti;
33074     },
33075
33076     updatePanelTitle : function(panel, title)
33077     {
33078         if(this.activePanel == panel){
33079             this.updateTitle(title);
33080         }
33081         if(this.tabs){
33082             var ti = this.tabs.getTab(panel.getEl().id);
33083             ti.setText(title);
33084             if(panel.tabTip !== undefined){
33085                 ti.setTooltip(panel.tabTip);
33086             }
33087         }
33088     },
33089
33090     updateTitle : function(title){
33091         if(this.titleTextEl && !this.config.title){
33092             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33093         }
33094     },
33095
33096     setActivePanel : function(panel)
33097     {
33098         panel = this.getPanel(panel);
33099         if(this.activePanel && this.activePanel != panel){
33100             this.activePanel.setActiveState(false);
33101         }
33102         this.activePanel = panel;
33103         panel.setActiveState(true);
33104         if(this.panelSize){
33105             panel.setSize(this.panelSize.width, this.panelSize.height);
33106         }
33107         if(this.closeBtn){
33108             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33109         }
33110         this.updateTitle(panel.getTitle());
33111         if(this.tabs){
33112             this.fireEvent("invalidated", this);
33113         }
33114         this.fireEvent("panelactivated", this, panel);
33115     },
33116
33117     /**
33118      * Shows the specified panel.
33119      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33120      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33121      */
33122     showPanel : function(panel)
33123     {
33124         panel = this.getPanel(panel);
33125         if(panel){
33126             if(this.tabs){
33127                 var tab = this.tabs.getTab(panel.getEl().id);
33128                 if(tab.isHidden()){
33129                     this.tabs.unhideTab(tab.id);
33130                 }
33131                 tab.activate();
33132             }else{
33133                 this.setActivePanel(panel);
33134             }
33135         }
33136         return panel;
33137     },
33138
33139     /**
33140      * Get the active panel for this region.
33141      * @return {Roo.ContentPanel} The active panel or null
33142      */
33143     getActivePanel : function(){
33144         return this.activePanel;
33145     },
33146
33147     validateVisibility : function(){
33148         if(this.panels.getCount() < 1){
33149             this.updateTitle("&#160;");
33150             this.closeBtn.hide();
33151             this.hide();
33152         }else{
33153             if(!this.isVisible()){
33154                 this.show();
33155             }
33156         }
33157     },
33158
33159     /**
33160      * Adds the passed ContentPanel(s) to this region.
33161      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33162      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33163      */
33164     add : function(panel)
33165     {
33166         if(arguments.length > 1){
33167             for(var i = 0, len = arguments.length; i < len; i++) {
33168                 this.add(arguments[i]);
33169             }
33170             return null;
33171         }
33172         
33173         // if we have not been rendered yet, then we can not really do much of this..
33174         if (!this.bodyEl) {
33175             this.unrendered_panels.push(panel);
33176             return panel;
33177         }
33178         
33179         
33180         
33181         
33182         if(this.hasPanel(panel)){
33183             this.showPanel(panel);
33184             return panel;
33185         }
33186         panel.setRegion(this);
33187         this.panels.add(panel);
33188        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33189             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33190             // and hide them... ???
33191             this.bodyEl.dom.appendChild(panel.getEl().dom);
33192             if(panel.background !== true){
33193                 this.setActivePanel(panel);
33194             }
33195             this.fireEvent("paneladded", this, panel);
33196             return panel;
33197         }
33198         */
33199         if(!this.tabs){
33200             this.initTabs();
33201         }else{
33202             this.initPanelAsTab(panel);
33203         }
33204         
33205         
33206         if(panel.background !== true){
33207             this.tabs.activate(panel.getEl().id);
33208         }
33209         this.fireEvent("paneladded", this, panel);
33210         return panel;
33211     },
33212
33213     /**
33214      * Hides the tab for the specified panel.
33215      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33216      */
33217     hidePanel : function(panel){
33218         if(this.tabs && (panel = this.getPanel(panel))){
33219             this.tabs.hideTab(panel.getEl().id);
33220         }
33221     },
33222
33223     /**
33224      * Unhides the tab for a previously hidden panel.
33225      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33226      */
33227     unhidePanel : function(panel){
33228         if(this.tabs && (panel = this.getPanel(panel))){
33229             this.tabs.unhideTab(panel.getEl().id);
33230         }
33231     },
33232
33233     clearPanels : function(){
33234         while(this.panels.getCount() > 0){
33235              this.remove(this.panels.first());
33236         }
33237     },
33238
33239     /**
33240      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33241      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33242      * @param {Boolean} preservePanel Overrides the config preservePanel option
33243      * @return {Roo.ContentPanel} The panel that was removed
33244      */
33245     remove : function(panel, preservePanel)
33246     {
33247         panel = this.getPanel(panel);
33248         if(!panel){
33249             return null;
33250         }
33251         var e = {};
33252         this.fireEvent("beforeremove", this, panel, e);
33253         if(e.cancel === true){
33254             return null;
33255         }
33256         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33257         var panelId = panel.getId();
33258         this.panels.removeKey(panelId);
33259         if(preservePanel){
33260             document.body.appendChild(panel.getEl().dom);
33261         }
33262         if(this.tabs){
33263             this.tabs.removeTab(panel.getEl().id);
33264         }else if (!preservePanel){
33265             this.bodyEl.dom.removeChild(panel.getEl().dom);
33266         }
33267         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33268             var p = this.panels.first();
33269             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33270             tempEl.appendChild(p.getEl().dom);
33271             this.bodyEl.update("");
33272             this.bodyEl.dom.appendChild(p.getEl().dom);
33273             tempEl = null;
33274             this.updateTitle(p.getTitle());
33275             this.tabs = null;
33276             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33277             this.setActivePanel(p);
33278         }
33279         panel.setRegion(null);
33280         if(this.activePanel == panel){
33281             this.activePanel = null;
33282         }
33283         if(this.config.autoDestroy !== false && preservePanel !== true){
33284             try{panel.destroy();}catch(e){}
33285         }
33286         this.fireEvent("panelremoved", this, panel);
33287         return panel;
33288     },
33289
33290     /**
33291      * Returns the TabPanel component used by this region
33292      * @return {Roo.TabPanel}
33293      */
33294     getTabs : function(){
33295         return this.tabs;
33296     },
33297
33298     createTool : function(parentEl, className){
33299         var btn = Roo.DomHelper.append(parentEl, {
33300             tag: "div",
33301             cls: "x-layout-tools-button",
33302             children: [ {
33303                 tag: "div",
33304                 cls: "roo-layout-tools-button-inner " + className,
33305                 html: "&#160;"
33306             }]
33307         }, true);
33308         btn.addClassOnOver("roo-layout-tools-button-over");
33309         return btn;
33310     }
33311 });/*
33312  * Based on:
33313  * Ext JS Library 1.1.1
33314  * Copyright(c) 2006-2007, Ext JS, LLC.
33315  *
33316  * Originally Released Under LGPL - original licence link has changed is not relivant.
33317  *
33318  * Fork - LGPL
33319  * <script type="text/javascript">
33320  */
33321  
33322
33323
33324 /**
33325  * @class Roo.SplitLayoutRegion
33326  * @extends Roo.LayoutRegion
33327  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33328  */
33329 Roo.bootstrap.layout.Split = function(config){
33330     this.cursor = config.cursor;
33331     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33332 };
33333
33334 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33335 {
33336     splitTip : "Drag to resize.",
33337     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33338     useSplitTips : false,
33339
33340     applyConfig : function(config){
33341         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33342     },
33343     
33344     onRender : function(ctr,pos) {
33345         
33346         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33347         if(!this.config.split){
33348             return;
33349         }
33350         if(!this.split){
33351             
33352             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33353                             tag: "div",
33354                             id: this.el.id + "-split",
33355                             cls: "roo-layout-split roo-layout-split-"+this.position,
33356                             html: "&#160;"
33357             });
33358             /** The SplitBar for this region 
33359             * @type Roo.SplitBar */
33360             // does not exist yet...
33361             Roo.log([this.position, this.orientation]);
33362             
33363             this.split = new Roo.bootstrap.SplitBar({
33364                 dragElement : splitEl,
33365                 resizingElement: this.el,
33366                 orientation : this.orientation
33367             });
33368             
33369             this.split.on("moved", this.onSplitMove, this);
33370             this.split.useShim = this.config.useShim === true;
33371             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33372             if(this.useSplitTips){
33373                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33374             }
33375             //if(config.collapsible){
33376             //    this.split.el.on("dblclick", this.collapse,  this);
33377             //}
33378         }
33379         if(typeof this.config.minSize != "undefined"){
33380             this.split.minSize = this.config.minSize;
33381         }
33382         if(typeof this.config.maxSize != "undefined"){
33383             this.split.maxSize = this.config.maxSize;
33384         }
33385         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33386             this.hideSplitter();
33387         }
33388         
33389     },
33390
33391     getHMaxSize : function(){
33392          var cmax = this.config.maxSize || 10000;
33393          var center = this.mgr.getRegion("center");
33394          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33395     },
33396
33397     getVMaxSize : function(){
33398          var cmax = this.config.maxSize || 10000;
33399          var center = this.mgr.getRegion("center");
33400          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33401     },
33402
33403     onSplitMove : function(split, newSize){
33404         this.fireEvent("resized", this, newSize);
33405     },
33406     
33407     /** 
33408      * Returns the {@link Roo.SplitBar} for this region.
33409      * @return {Roo.SplitBar}
33410      */
33411     getSplitBar : function(){
33412         return this.split;
33413     },
33414     
33415     hide : function(){
33416         this.hideSplitter();
33417         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33418     },
33419
33420     hideSplitter : function(){
33421         if(this.split){
33422             this.split.el.setLocation(-2000,-2000);
33423             this.split.el.hide();
33424         }
33425     },
33426
33427     show : function(){
33428         if(this.split){
33429             this.split.el.show();
33430         }
33431         Roo.bootstrap.layout.Split.superclass.show.call(this);
33432     },
33433     
33434     beforeSlide: function(){
33435         if(Roo.isGecko){// firefox overflow auto bug workaround
33436             this.bodyEl.clip();
33437             if(this.tabs) {
33438                 this.tabs.bodyEl.clip();
33439             }
33440             if(this.activePanel){
33441                 this.activePanel.getEl().clip();
33442                 
33443                 if(this.activePanel.beforeSlide){
33444                     this.activePanel.beforeSlide();
33445                 }
33446             }
33447         }
33448     },
33449     
33450     afterSlide : function(){
33451         if(Roo.isGecko){// firefox overflow auto bug workaround
33452             this.bodyEl.unclip();
33453             if(this.tabs) {
33454                 this.tabs.bodyEl.unclip();
33455             }
33456             if(this.activePanel){
33457                 this.activePanel.getEl().unclip();
33458                 if(this.activePanel.afterSlide){
33459                     this.activePanel.afterSlide();
33460                 }
33461             }
33462         }
33463     },
33464
33465     initAutoHide : function(){
33466         if(this.autoHide !== false){
33467             if(!this.autoHideHd){
33468                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33469                 this.autoHideHd = {
33470                     "mouseout": function(e){
33471                         if(!e.within(this.el, true)){
33472                             st.delay(500);
33473                         }
33474                     },
33475                     "mouseover" : function(e){
33476                         st.cancel();
33477                     },
33478                     scope : this
33479                 };
33480             }
33481             this.el.on(this.autoHideHd);
33482         }
33483     },
33484
33485     clearAutoHide : function(){
33486         if(this.autoHide !== false){
33487             this.el.un("mouseout", this.autoHideHd.mouseout);
33488             this.el.un("mouseover", this.autoHideHd.mouseover);
33489         }
33490     },
33491
33492     clearMonitor : function(){
33493         Roo.get(document).un("click", this.slideInIf, this);
33494     },
33495
33496     // these names are backwards but not changed for compat
33497     slideOut : function(){
33498         if(this.isSlid || this.el.hasActiveFx()){
33499             return;
33500         }
33501         this.isSlid = true;
33502         if(this.collapseBtn){
33503             this.collapseBtn.hide();
33504         }
33505         this.closeBtnState = this.closeBtn.getStyle('display');
33506         this.closeBtn.hide();
33507         if(this.stickBtn){
33508             this.stickBtn.show();
33509         }
33510         this.el.show();
33511         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33512         this.beforeSlide();
33513         this.el.setStyle("z-index", 10001);
33514         this.el.slideIn(this.getSlideAnchor(), {
33515             callback: function(){
33516                 this.afterSlide();
33517                 this.initAutoHide();
33518                 Roo.get(document).on("click", this.slideInIf, this);
33519                 this.fireEvent("slideshow", this);
33520             },
33521             scope: this,
33522             block: true
33523         });
33524     },
33525
33526     afterSlideIn : function(){
33527         this.clearAutoHide();
33528         this.isSlid = false;
33529         this.clearMonitor();
33530         this.el.setStyle("z-index", "");
33531         if(this.collapseBtn){
33532             this.collapseBtn.show();
33533         }
33534         this.closeBtn.setStyle('display', this.closeBtnState);
33535         if(this.stickBtn){
33536             this.stickBtn.hide();
33537         }
33538         this.fireEvent("slidehide", this);
33539     },
33540
33541     slideIn : function(cb){
33542         if(!this.isSlid || this.el.hasActiveFx()){
33543             Roo.callback(cb);
33544             return;
33545         }
33546         this.isSlid = false;
33547         this.beforeSlide();
33548         this.el.slideOut(this.getSlideAnchor(), {
33549             callback: function(){
33550                 this.el.setLeftTop(-10000, -10000);
33551                 this.afterSlide();
33552                 this.afterSlideIn();
33553                 Roo.callback(cb);
33554             },
33555             scope: this,
33556             block: true
33557         });
33558     },
33559     
33560     slideInIf : function(e){
33561         if(!e.within(this.el)){
33562             this.slideIn();
33563         }
33564     },
33565
33566     animateCollapse : function(){
33567         this.beforeSlide();
33568         this.el.setStyle("z-index", 20000);
33569         var anchor = this.getSlideAnchor();
33570         this.el.slideOut(anchor, {
33571             callback : function(){
33572                 this.el.setStyle("z-index", "");
33573                 this.collapsedEl.slideIn(anchor, {duration:.3});
33574                 this.afterSlide();
33575                 this.el.setLocation(-10000,-10000);
33576                 this.el.hide();
33577                 this.fireEvent("collapsed", this);
33578             },
33579             scope: this,
33580             block: true
33581         });
33582     },
33583
33584     animateExpand : function(){
33585         this.beforeSlide();
33586         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33587         this.el.setStyle("z-index", 20000);
33588         this.collapsedEl.hide({
33589             duration:.1
33590         });
33591         this.el.slideIn(this.getSlideAnchor(), {
33592             callback : function(){
33593                 this.el.setStyle("z-index", "");
33594                 this.afterSlide();
33595                 if(this.split){
33596                     this.split.el.show();
33597                 }
33598                 this.fireEvent("invalidated", this);
33599                 this.fireEvent("expanded", this);
33600             },
33601             scope: this,
33602             block: true
33603         });
33604     },
33605
33606     anchors : {
33607         "west" : "left",
33608         "east" : "right",
33609         "north" : "top",
33610         "south" : "bottom"
33611     },
33612
33613     sanchors : {
33614         "west" : "l",
33615         "east" : "r",
33616         "north" : "t",
33617         "south" : "b"
33618     },
33619
33620     canchors : {
33621         "west" : "tl-tr",
33622         "east" : "tr-tl",
33623         "north" : "tl-bl",
33624         "south" : "bl-tl"
33625     },
33626
33627     getAnchor : function(){
33628         return this.anchors[this.position];
33629     },
33630
33631     getCollapseAnchor : function(){
33632         return this.canchors[this.position];
33633     },
33634
33635     getSlideAnchor : function(){
33636         return this.sanchors[this.position];
33637     },
33638
33639     getAlignAdj : function(){
33640         var cm = this.cmargins;
33641         switch(this.position){
33642             case "west":
33643                 return [0, 0];
33644             break;
33645             case "east":
33646                 return [0, 0];
33647             break;
33648             case "north":
33649                 return [0, 0];
33650             break;
33651             case "south":
33652                 return [0, 0];
33653             break;
33654         }
33655     },
33656
33657     getExpandAdj : function(){
33658         var c = this.collapsedEl, cm = this.cmargins;
33659         switch(this.position){
33660             case "west":
33661                 return [-(cm.right+c.getWidth()+cm.left), 0];
33662             break;
33663             case "east":
33664                 return [cm.right+c.getWidth()+cm.left, 0];
33665             break;
33666             case "north":
33667                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33668             break;
33669             case "south":
33670                 return [0, cm.top+cm.bottom+c.getHeight()];
33671             break;
33672         }
33673     }
33674 });/*
33675  * Based on:
33676  * Ext JS Library 1.1.1
33677  * Copyright(c) 2006-2007, Ext JS, LLC.
33678  *
33679  * Originally Released Under LGPL - original licence link has changed is not relivant.
33680  *
33681  * Fork - LGPL
33682  * <script type="text/javascript">
33683  */
33684 /*
33685  * These classes are private internal classes
33686  */
33687 Roo.bootstrap.layout.Center = function(config){
33688     config.region = "center";
33689     Roo.bootstrap.layout.Region.call(this, config);
33690     this.visible = true;
33691     this.minWidth = config.minWidth || 20;
33692     this.minHeight = config.minHeight || 20;
33693 };
33694
33695 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33696     hide : function(){
33697         // center panel can't be hidden
33698     },
33699     
33700     show : function(){
33701         // center panel can't be hidden
33702     },
33703     
33704     getMinWidth: function(){
33705         return this.minWidth;
33706     },
33707     
33708     getMinHeight: function(){
33709         return this.minHeight;
33710     }
33711 });
33712
33713
33714
33715
33716  
33717
33718
33719
33720
33721
33722 Roo.bootstrap.layout.North = function(config)
33723 {
33724     config.region = 'north';
33725     config.cursor = 'n-resize';
33726     
33727     Roo.bootstrap.layout.Split.call(this, config);
33728     
33729     
33730     if(this.split){
33731         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33732         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33733         this.split.el.addClass("roo-layout-split-v");
33734     }
33735     var size = config.initialSize || config.height;
33736     if(typeof size != "undefined"){
33737         this.el.setHeight(size);
33738     }
33739 };
33740 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33741 {
33742     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33743     
33744     
33745     
33746     getBox : function(){
33747         if(this.collapsed){
33748             return this.collapsedEl.getBox();
33749         }
33750         var box = this.el.getBox();
33751         if(this.split){
33752             box.height += this.split.el.getHeight();
33753         }
33754         return box;
33755     },
33756     
33757     updateBox : function(box){
33758         if(this.split && !this.collapsed){
33759             box.height -= this.split.el.getHeight();
33760             this.split.el.setLeft(box.x);
33761             this.split.el.setTop(box.y+box.height);
33762             this.split.el.setWidth(box.width);
33763         }
33764         if(this.collapsed){
33765             this.updateBody(box.width, null);
33766         }
33767         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33768     }
33769 });
33770
33771
33772
33773
33774
33775 Roo.bootstrap.layout.South = function(config){
33776     config.region = 'south';
33777     config.cursor = 's-resize';
33778     Roo.bootstrap.layout.Split.call(this, config);
33779     if(this.split){
33780         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33781         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33782         this.split.el.addClass("roo-layout-split-v");
33783     }
33784     var size = config.initialSize || config.height;
33785     if(typeof size != "undefined"){
33786         this.el.setHeight(size);
33787     }
33788 };
33789
33790 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33791     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33792     getBox : function(){
33793         if(this.collapsed){
33794             return this.collapsedEl.getBox();
33795         }
33796         var box = this.el.getBox();
33797         if(this.split){
33798             var sh = this.split.el.getHeight();
33799             box.height += sh;
33800             box.y -= sh;
33801         }
33802         return box;
33803     },
33804     
33805     updateBox : function(box){
33806         if(this.split && !this.collapsed){
33807             var sh = this.split.el.getHeight();
33808             box.height -= sh;
33809             box.y += sh;
33810             this.split.el.setLeft(box.x);
33811             this.split.el.setTop(box.y-sh);
33812             this.split.el.setWidth(box.width);
33813         }
33814         if(this.collapsed){
33815             this.updateBody(box.width, null);
33816         }
33817         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33818     }
33819 });
33820
33821 Roo.bootstrap.layout.East = function(config){
33822     config.region = "east";
33823     config.cursor = "e-resize";
33824     Roo.bootstrap.layout.Split.call(this, config);
33825     if(this.split){
33826         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33827         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33828         this.split.el.addClass("roo-layout-split-h");
33829     }
33830     var size = config.initialSize || config.width;
33831     if(typeof size != "undefined"){
33832         this.el.setWidth(size);
33833     }
33834 };
33835 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33836     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33837     getBox : function(){
33838         if(this.collapsed){
33839             return this.collapsedEl.getBox();
33840         }
33841         var box = this.el.getBox();
33842         if(this.split){
33843             var sw = this.split.el.getWidth();
33844             box.width += sw;
33845             box.x -= sw;
33846         }
33847         return box;
33848     },
33849
33850     updateBox : function(box){
33851         if(this.split && !this.collapsed){
33852             var sw = this.split.el.getWidth();
33853             box.width -= sw;
33854             this.split.el.setLeft(box.x);
33855             this.split.el.setTop(box.y);
33856             this.split.el.setHeight(box.height);
33857             box.x += sw;
33858         }
33859         if(this.collapsed){
33860             this.updateBody(null, box.height);
33861         }
33862         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33863     }
33864 });
33865
33866 Roo.bootstrap.layout.West = function(config){
33867     config.region = "west";
33868     config.cursor = "w-resize";
33869     
33870     Roo.bootstrap.layout.Split.call(this, config);
33871     if(this.split){
33872         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33873         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33874         this.split.el.addClass("roo-layout-split-h");
33875     }
33876     
33877 };
33878 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33879     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33880     
33881     onRender: function(ctr, pos)
33882     {
33883         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33884         var size = this.config.initialSize || this.config.width;
33885         if(typeof size != "undefined"){
33886             this.el.setWidth(size);
33887         }
33888     },
33889     
33890     getBox : function(){
33891         if(this.collapsed){
33892             return this.collapsedEl.getBox();
33893         }
33894         var box = this.el.getBox();
33895         if(this.split){
33896             box.width += this.split.el.getWidth();
33897         }
33898         return box;
33899     },
33900     
33901     updateBox : function(box){
33902         if(this.split && !this.collapsed){
33903             var sw = this.split.el.getWidth();
33904             box.width -= sw;
33905             this.split.el.setLeft(box.x+box.width);
33906             this.split.el.setTop(box.y);
33907             this.split.el.setHeight(box.height);
33908         }
33909         if(this.collapsed){
33910             this.updateBody(null, box.height);
33911         }
33912         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33913     }
33914 });
33915 Roo.namespace("Roo.bootstrap.panel");/*
33916  * Based on:
33917  * Ext JS Library 1.1.1
33918  * Copyright(c) 2006-2007, Ext JS, LLC.
33919  *
33920  * Originally Released Under LGPL - original licence link has changed is not relivant.
33921  *
33922  * Fork - LGPL
33923  * <script type="text/javascript">
33924  */
33925 /**
33926  * @class Roo.ContentPanel
33927  * @extends Roo.util.Observable
33928  * A basic ContentPanel element.
33929  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33930  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33931  * @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
33932  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33933  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33934  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33935  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33936  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33937  * @cfg {String} title          The title for this panel
33938  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33939  * @cfg {String} url            Calls {@link #setUrl} with this value
33940  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33941  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33942  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33943  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33944
33945  * @constructor
33946  * Create a new ContentPanel.
33947  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33948  * @param {String/Object} config A string to set only the title or a config object
33949  * @param {String} content (optional) Set the HTML content for this panel
33950  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33951  */
33952 Roo.bootstrap.panel.Content = function( config){
33953     
33954     var el = config.el;
33955     var content = config.content;
33956
33957     if(config.autoCreate){ // xtype is available if this is called from factory
33958         el = Roo.id();
33959     }
33960     this.el = Roo.get(el);
33961     if(!this.el && config && config.autoCreate){
33962         if(typeof config.autoCreate == "object"){
33963             if(!config.autoCreate.id){
33964                 config.autoCreate.id = config.id||el;
33965             }
33966             this.el = Roo.DomHelper.append(document.body,
33967                         config.autoCreate, true);
33968         }else{
33969             var elcfg =  {   tag: "div",
33970                             cls: "roo-layout-inactive-content",
33971                             id: config.id||el
33972                             };
33973             if (config.html) {
33974                 elcfg.html = config.html;
33975                 
33976             }
33977                         
33978             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33979         }
33980     } 
33981     this.closable = false;
33982     this.loaded = false;
33983     this.active = false;
33984    
33985       
33986     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33987         
33988         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33989         
33990         this.wrapEl = this.el.wrap();
33991         var ti = [];
33992         if (config.toolbar.items) {
33993             ti = config.toolbar.items ;
33994             delete config.toolbar.items ;
33995         }
33996         
33997         var nitems = [];
33998         this.toolbar.render(this.wrapEl, 'before');
33999         for(var i =0;i < ti.length;i++) {
34000           //  Roo.log(['add child', items[i]]);
34001             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34002         }
34003         this.toolbar.items = nitems;
34004         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34005         delete config.toolbar;
34006         
34007     }
34008     /*
34009     // xtype created footer. - not sure if will work as we normally have to render first..
34010     if (this.footer && !this.footer.el && this.footer.xtype) {
34011         if (!this.wrapEl) {
34012             this.wrapEl = this.el.wrap();
34013         }
34014     
34015         this.footer.container = this.wrapEl.createChild();
34016          
34017         this.footer = Roo.factory(this.footer, Roo);
34018         
34019     }
34020     */
34021     
34022      if(typeof config == "string"){
34023         this.title = config;
34024     }else{
34025         Roo.apply(this, config);
34026     }
34027     
34028     if(this.resizeEl){
34029         this.resizeEl = Roo.get(this.resizeEl, true);
34030     }else{
34031         this.resizeEl = this.el;
34032     }
34033     // handle view.xtype
34034     
34035  
34036     
34037     
34038     this.addEvents({
34039         /**
34040          * @event activate
34041          * Fires when this panel is activated. 
34042          * @param {Roo.ContentPanel} this
34043          */
34044         "activate" : true,
34045         /**
34046          * @event deactivate
34047          * Fires when this panel is activated. 
34048          * @param {Roo.ContentPanel} this
34049          */
34050         "deactivate" : true,
34051
34052         /**
34053          * @event resize
34054          * Fires when this panel is resized if fitToFrame is true.
34055          * @param {Roo.ContentPanel} this
34056          * @param {Number} width The width after any component adjustments
34057          * @param {Number} height The height after any component adjustments
34058          */
34059         "resize" : true,
34060         
34061          /**
34062          * @event render
34063          * Fires when this tab is created
34064          * @param {Roo.ContentPanel} this
34065          */
34066         "render" : true
34067         
34068         
34069         
34070     });
34071     
34072
34073     
34074     
34075     if(this.autoScroll){
34076         this.resizeEl.setStyle("overflow", "auto");
34077     } else {
34078         // fix randome scrolling
34079         //this.el.on('scroll', function() {
34080         //    Roo.log('fix random scolling');
34081         //    this.scrollTo('top',0); 
34082         //});
34083     }
34084     content = content || this.content;
34085     if(content){
34086         this.setContent(content);
34087     }
34088     if(config && config.url){
34089         this.setUrl(this.url, this.params, this.loadOnce);
34090     }
34091     
34092     
34093     
34094     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34095     
34096     if (this.view && typeof(this.view.xtype) != 'undefined') {
34097         this.view.el = this.el.appendChild(document.createElement("div"));
34098         this.view = Roo.factory(this.view); 
34099         this.view.render  &&  this.view.render(false, '');  
34100     }
34101     
34102     
34103     this.fireEvent('render', this);
34104 };
34105
34106 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34107     tabTip:'',
34108     setRegion : function(region){
34109         this.region = region;
34110         if(region){
34111            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34112         }else{
34113            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34114         } 
34115     },
34116     
34117     /**
34118      * Returns the toolbar for this Panel if one was configured. 
34119      * @return {Roo.Toolbar} 
34120      */
34121     getToolbar : function(){
34122         return this.toolbar;
34123     },
34124     
34125     setActiveState : function(active){
34126         this.active = active;
34127         if(!active){
34128             this.fireEvent("deactivate", this);
34129         }else{
34130             this.fireEvent("activate", this);
34131         }
34132     },
34133     /**
34134      * Updates this panel's element
34135      * @param {String} content The new content
34136      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34137     */
34138     setContent : function(content, loadScripts){
34139         this.el.update(content, loadScripts);
34140     },
34141
34142     ignoreResize : function(w, h){
34143         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34144             return true;
34145         }else{
34146             this.lastSize = {width: w, height: h};
34147             return false;
34148         }
34149     },
34150     /**
34151      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34152      * @return {Roo.UpdateManager} The UpdateManager
34153      */
34154     getUpdateManager : function(){
34155         return this.el.getUpdateManager();
34156     },
34157      /**
34158      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34159      * @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:
34160 <pre><code>
34161 panel.load({
34162     url: "your-url.php",
34163     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34164     callback: yourFunction,
34165     scope: yourObject, //(optional scope)
34166     discardUrl: false,
34167     nocache: false,
34168     text: "Loading...",
34169     timeout: 30,
34170     scripts: false
34171 });
34172 </code></pre>
34173      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34174      * 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.
34175      * @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}
34176      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34177      * @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.
34178      * @return {Roo.ContentPanel} this
34179      */
34180     load : function(){
34181         var um = this.el.getUpdateManager();
34182         um.update.apply(um, arguments);
34183         return this;
34184     },
34185
34186
34187     /**
34188      * 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.
34189      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34190      * @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)
34191      * @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)
34192      * @return {Roo.UpdateManager} The UpdateManager
34193      */
34194     setUrl : function(url, params, loadOnce){
34195         if(this.refreshDelegate){
34196             this.removeListener("activate", this.refreshDelegate);
34197         }
34198         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34199         this.on("activate", this.refreshDelegate);
34200         return this.el.getUpdateManager();
34201     },
34202     
34203     _handleRefresh : function(url, params, loadOnce){
34204         if(!loadOnce || !this.loaded){
34205             var updater = this.el.getUpdateManager();
34206             updater.update(url, params, this._setLoaded.createDelegate(this));
34207         }
34208     },
34209     
34210     _setLoaded : function(){
34211         this.loaded = true;
34212     }, 
34213     
34214     /**
34215      * Returns this panel's id
34216      * @return {String} 
34217      */
34218     getId : function(){
34219         return this.el.id;
34220     },
34221     
34222     /** 
34223      * Returns this panel's element - used by regiosn to add.
34224      * @return {Roo.Element} 
34225      */
34226     getEl : function(){
34227         return this.wrapEl || this.el;
34228     },
34229     
34230    
34231     
34232     adjustForComponents : function(width, height)
34233     {
34234         //Roo.log('adjustForComponents ');
34235         if(this.resizeEl != this.el){
34236             width -= this.el.getFrameWidth('lr');
34237             height -= this.el.getFrameWidth('tb');
34238         }
34239         if(this.toolbar){
34240             var te = this.toolbar.getEl();
34241             height -= te.getHeight();
34242             te.setWidth(width);
34243         }
34244         if(this.footer){
34245             var te = this.footer.getEl();
34246             Roo.log("footer:" + te.getHeight());
34247             
34248             height -= te.getHeight();
34249             te.setWidth(width);
34250         }
34251         
34252         
34253         if(this.adjustments){
34254             width += this.adjustments[0];
34255             height += this.adjustments[1];
34256         }
34257         return {"width": width, "height": height};
34258     },
34259     
34260     setSize : function(width, height){
34261         if(this.fitToFrame && !this.ignoreResize(width, height)){
34262             if(this.fitContainer && this.resizeEl != this.el){
34263                 this.el.setSize(width, height);
34264             }
34265             var size = this.adjustForComponents(width, height);
34266             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34267             this.fireEvent('resize', this, size.width, size.height);
34268         }
34269     },
34270     
34271     /**
34272      * Returns this panel's title
34273      * @return {String} 
34274      */
34275     getTitle : function(){
34276         return this.title;
34277     },
34278     
34279     /**
34280      * Set this panel's title
34281      * @param {String} title
34282      */
34283     setTitle : function(title){
34284         this.title = title;
34285         if(this.region){
34286             this.region.updatePanelTitle(this, title);
34287         }
34288     },
34289     
34290     /**
34291      * Returns true is this panel was configured to be closable
34292      * @return {Boolean} 
34293      */
34294     isClosable : function(){
34295         return this.closable;
34296     },
34297     
34298     beforeSlide : function(){
34299         this.el.clip();
34300         this.resizeEl.clip();
34301     },
34302     
34303     afterSlide : function(){
34304         this.el.unclip();
34305         this.resizeEl.unclip();
34306     },
34307     
34308     /**
34309      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34310      *   Will fail silently if the {@link #setUrl} method has not been called.
34311      *   This does not activate the panel, just updates its content.
34312      */
34313     refresh : function(){
34314         if(this.refreshDelegate){
34315            this.loaded = false;
34316            this.refreshDelegate();
34317         }
34318     },
34319     
34320     /**
34321      * Destroys this panel
34322      */
34323     destroy : function(){
34324         this.el.removeAllListeners();
34325         var tempEl = document.createElement("span");
34326         tempEl.appendChild(this.el.dom);
34327         tempEl.innerHTML = "";
34328         this.el.remove();
34329         this.el = null;
34330     },
34331     
34332     /**
34333      * form - if the content panel contains a form - this is a reference to it.
34334      * @type {Roo.form.Form}
34335      */
34336     form : false,
34337     /**
34338      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34339      *    This contains a reference to it.
34340      * @type {Roo.View}
34341      */
34342     view : false,
34343     
34344       /**
34345      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34346      * <pre><code>
34347
34348 layout.addxtype({
34349        xtype : 'Form',
34350        items: [ .... ]
34351    }
34352 );
34353
34354 </code></pre>
34355      * @param {Object} cfg Xtype definition of item to add.
34356      */
34357     
34358     
34359     getChildContainer: function () {
34360         return this.getEl();
34361     }
34362     
34363     
34364     /*
34365         var  ret = new Roo.factory(cfg);
34366         return ret;
34367         
34368         
34369         // add form..
34370         if (cfg.xtype.match(/^Form$/)) {
34371             
34372             var el;
34373             //if (this.footer) {
34374             //    el = this.footer.container.insertSibling(false, 'before');
34375             //} else {
34376                 el = this.el.createChild();
34377             //}
34378
34379             this.form = new  Roo.form.Form(cfg);
34380             
34381             
34382             if ( this.form.allItems.length) {
34383                 this.form.render(el.dom);
34384             }
34385             return this.form;
34386         }
34387         // should only have one of theses..
34388         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34389             // views.. should not be just added - used named prop 'view''
34390             
34391             cfg.el = this.el.appendChild(document.createElement("div"));
34392             // factory?
34393             
34394             var ret = new Roo.factory(cfg);
34395              
34396              ret.render && ret.render(false, ''); // render blank..
34397             this.view = ret;
34398             return ret;
34399         }
34400         return false;
34401     }
34402     \*/
34403 });
34404  
34405 /**
34406  * @class Roo.bootstrap.panel.Grid
34407  * @extends Roo.bootstrap.panel.Content
34408  * @constructor
34409  * Create a new GridPanel.
34410  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34411  * @param {Object} config A the config object
34412   
34413  */
34414
34415
34416
34417 Roo.bootstrap.panel.Grid = function(config)
34418 {
34419     
34420       
34421     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34422         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34423
34424     config.el = this.wrapper;
34425     //this.el = this.wrapper;
34426     
34427       if (config.container) {
34428         // ctor'ed from a Border/panel.grid
34429         
34430         
34431         this.wrapper.setStyle("overflow", "hidden");
34432         this.wrapper.addClass('roo-grid-container');
34433
34434     }
34435     
34436     
34437     if(config.toolbar){
34438         var tool_el = this.wrapper.createChild();    
34439         this.toolbar = Roo.factory(config.toolbar);
34440         var ti = [];
34441         if (config.toolbar.items) {
34442             ti = config.toolbar.items ;
34443             delete config.toolbar.items ;
34444         }
34445         
34446         var nitems = [];
34447         this.toolbar.render(tool_el);
34448         for(var i =0;i < ti.length;i++) {
34449           //  Roo.log(['add child', items[i]]);
34450             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34451         }
34452         this.toolbar.items = nitems;
34453         
34454         delete config.toolbar;
34455     }
34456     
34457     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34458     config.grid.scrollBody = true;;
34459     config.grid.monitorWindowResize = false; // turn off autosizing
34460     config.grid.autoHeight = false;
34461     config.grid.autoWidth = false;
34462     
34463     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34464     
34465     if (config.background) {
34466         // render grid on panel activation (if panel background)
34467         this.on('activate', function(gp) {
34468             if (!gp.grid.rendered) {
34469                 gp.grid.render(el);
34470                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34471
34472             }
34473         });
34474             
34475     } else {
34476         this.grid.render(this.wrapper);
34477         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34478
34479     }
34480     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34481     // ??? needed ??? config.el = this.wrapper;
34482     
34483     
34484     
34485   
34486     // xtype created footer. - not sure if will work as we normally have to render first..
34487     if (this.footer && !this.footer.el && this.footer.xtype) {
34488         
34489         var ctr = this.grid.getView().getFooterPanel(true);
34490         this.footer.dataSource = this.grid.dataSource;
34491         this.footer = Roo.factory(this.footer, Roo);
34492         this.footer.render(ctr);
34493         
34494     }
34495     
34496     
34497     
34498     
34499      
34500 };
34501
34502 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34503     getId : function(){
34504         return this.grid.id;
34505     },
34506     
34507     /**
34508      * Returns the grid for this panel
34509      * @return {Roo.bootstrap.Table} 
34510      */
34511     getGrid : function(){
34512         return this.grid;    
34513     },
34514     
34515     setSize : function(width, height){
34516         if(!this.ignoreResize(width, height)){
34517             var grid = this.grid;
34518             var size = this.adjustForComponents(width, height);
34519             var gridel = grid.getGridEl();
34520             gridel.setSize(size.width, size.height);
34521             /*
34522             var thd = grid.getGridEl().select('thead',true).first();
34523             var tbd = grid.getGridEl().select('tbody', true).first();
34524             if (tbd) {
34525                 tbd.setSize(width, height - thd.getHeight());
34526             }
34527             */
34528             grid.autoSize();
34529         }
34530     },
34531      
34532     
34533     
34534     beforeSlide : function(){
34535         this.grid.getView().scroller.clip();
34536     },
34537     
34538     afterSlide : function(){
34539         this.grid.getView().scroller.unclip();
34540     },
34541     
34542     destroy : function(){
34543         this.grid.destroy();
34544         delete this.grid;
34545         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34546     }
34547 });
34548
34549 /**
34550  * @class Roo.bootstrap.panel.Nest
34551  * @extends Roo.bootstrap.panel.Content
34552  * @constructor
34553  * Create a new Panel, that can contain a layout.Border.
34554  * 
34555  * 
34556  * @param {Roo.BorderLayout} layout The layout for this panel
34557  * @param {String/Object} config A string to set only the title or a config object
34558  */
34559 Roo.bootstrap.panel.Nest = function(config)
34560 {
34561     // construct with only one argument..
34562     /* FIXME - implement nicer consturctors
34563     if (layout.layout) {
34564         config = layout;
34565         layout = config.layout;
34566         delete config.layout;
34567     }
34568     if (layout.xtype && !layout.getEl) {
34569         // then layout needs constructing..
34570         layout = Roo.factory(layout, Roo);
34571     }
34572     */
34573     
34574     config.el =  config.layout.getEl();
34575     
34576     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34577     
34578     config.layout.monitorWindowResize = false; // turn off autosizing
34579     this.layout = config.layout;
34580     this.layout.getEl().addClass("roo-layout-nested-layout");
34581     
34582     
34583     
34584     
34585 };
34586
34587 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34588
34589     setSize : function(width, height){
34590         if(!this.ignoreResize(width, height)){
34591             var size = this.adjustForComponents(width, height);
34592             var el = this.layout.getEl();
34593             if (size.height < 1) {
34594                 el.setWidth(size.width);   
34595             } else {
34596                 el.setSize(size.width, size.height);
34597             }
34598             var touch = el.dom.offsetWidth;
34599             this.layout.layout();
34600             // ie requires a double layout on the first pass
34601             if(Roo.isIE && !this.initialized){
34602                 this.initialized = true;
34603                 this.layout.layout();
34604             }
34605         }
34606     },
34607     
34608     // activate all subpanels if not currently active..
34609     
34610     setActiveState : function(active){
34611         this.active = active;
34612         if(!active){
34613             this.fireEvent("deactivate", this);
34614             return;
34615         }
34616         
34617         this.fireEvent("activate", this);
34618         // not sure if this should happen before or after..
34619         if (!this.layout) {
34620             return; // should not happen..
34621         }
34622         var reg = false;
34623         for (var r in this.layout.regions) {
34624             reg = this.layout.getRegion(r);
34625             if (reg.getActivePanel()) {
34626                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34627                 reg.setActivePanel(reg.getActivePanel());
34628                 continue;
34629             }
34630             if (!reg.panels.length) {
34631                 continue;
34632             }
34633             reg.showPanel(reg.getPanel(0));
34634         }
34635         
34636         
34637         
34638         
34639     },
34640     
34641     /**
34642      * Returns the nested BorderLayout for this panel
34643      * @return {Roo.BorderLayout} 
34644      */
34645     getLayout : function(){
34646         return this.layout;
34647     },
34648     
34649      /**
34650      * Adds a xtype elements to the layout of the nested panel
34651      * <pre><code>
34652
34653 panel.addxtype({
34654        xtype : 'ContentPanel',
34655        region: 'west',
34656        items: [ .... ]
34657    }
34658 );
34659
34660 panel.addxtype({
34661         xtype : 'NestedLayoutPanel',
34662         region: 'west',
34663         layout: {
34664            center: { },
34665            west: { }   
34666         },
34667         items : [ ... list of content panels or nested layout panels.. ]
34668    }
34669 );
34670 </code></pre>
34671      * @param {Object} cfg Xtype definition of item to add.
34672      */
34673     addxtype : function(cfg) {
34674         return this.layout.addxtype(cfg);
34675     
34676     }
34677 });        /*
34678  * Based on:
34679  * Ext JS Library 1.1.1
34680  * Copyright(c) 2006-2007, Ext JS, LLC.
34681  *
34682  * Originally Released Under LGPL - original licence link has changed is not relivant.
34683  *
34684  * Fork - LGPL
34685  * <script type="text/javascript">
34686  */
34687 /**
34688  * @class Roo.TabPanel
34689  * @extends Roo.util.Observable
34690  * A lightweight tab container.
34691  * <br><br>
34692  * Usage:
34693  * <pre><code>
34694 // basic tabs 1, built from existing content
34695 var tabs = new Roo.TabPanel("tabs1");
34696 tabs.addTab("script", "View Script");
34697 tabs.addTab("markup", "View Markup");
34698 tabs.activate("script");
34699
34700 // more advanced tabs, built from javascript
34701 var jtabs = new Roo.TabPanel("jtabs");
34702 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34703
34704 // set up the UpdateManager
34705 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34706 var updater = tab2.getUpdateManager();
34707 updater.setDefaultUrl("ajax1.htm");
34708 tab2.on('activate', updater.refresh, updater, true);
34709
34710 // Use setUrl for Ajax loading
34711 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34712 tab3.setUrl("ajax2.htm", null, true);
34713
34714 // Disabled tab
34715 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34716 tab4.disable();
34717
34718 jtabs.activate("jtabs-1");
34719  * </code></pre>
34720  * @constructor
34721  * Create a new TabPanel.
34722  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34723  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34724  */
34725 Roo.bootstrap.panel.Tabs = function(config){
34726     /**
34727     * The container element for this TabPanel.
34728     * @type Roo.Element
34729     */
34730     this.el = Roo.get(config.el);
34731     delete config.el;
34732     if(config){
34733         if(typeof config == "boolean"){
34734             this.tabPosition = config ? "bottom" : "top";
34735         }else{
34736             Roo.apply(this, config);
34737         }
34738     }
34739     
34740     if(this.tabPosition == "bottom"){
34741         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34742         this.el.addClass("roo-tabs-bottom");
34743     }
34744     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34745     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34746     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34747     if(Roo.isIE){
34748         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34749     }
34750     if(this.tabPosition != "bottom"){
34751         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34752          * @type Roo.Element
34753          */
34754         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34755         this.el.addClass("roo-tabs-top");
34756     }
34757     this.items = [];
34758
34759     this.bodyEl.setStyle("position", "relative");
34760
34761     this.active = null;
34762     this.activateDelegate = this.activate.createDelegate(this);
34763
34764     this.addEvents({
34765         /**
34766          * @event tabchange
34767          * Fires when the active tab changes
34768          * @param {Roo.TabPanel} this
34769          * @param {Roo.TabPanelItem} activePanel The new active tab
34770          */
34771         "tabchange": true,
34772         /**
34773          * @event beforetabchange
34774          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34775          * @param {Roo.TabPanel} this
34776          * @param {Object} e Set cancel to true on this object to cancel the tab change
34777          * @param {Roo.TabPanelItem} tab The tab being changed to
34778          */
34779         "beforetabchange" : true
34780     });
34781
34782     Roo.EventManager.onWindowResize(this.onResize, this);
34783     this.cpad = this.el.getPadding("lr");
34784     this.hiddenCount = 0;
34785
34786
34787     // toolbar on the tabbar support...
34788     if (this.toolbar) {
34789         alert("no toolbar support yet");
34790         this.toolbar  = false;
34791         /*
34792         var tcfg = this.toolbar;
34793         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34794         this.toolbar = new Roo.Toolbar(tcfg);
34795         if (Roo.isSafari) {
34796             var tbl = tcfg.container.child('table', true);
34797             tbl.setAttribute('width', '100%');
34798         }
34799         */
34800         
34801     }
34802    
34803
34804
34805     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34806 };
34807
34808 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34809     /*
34810      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34811      */
34812     tabPosition : "top",
34813     /*
34814      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34815      */
34816     currentTabWidth : 0,
34817     /*
34818      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34819      */
34820     minTabWidth : 40,
34821     /*
34822      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34823      */
34824     maxTabWidth : 250,
34825     /*
34826      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34827      */
34828     preferredTabWidth : 175,
34829     /*
34830      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34831      */
34832     resizeTabs : false,
34833     /*
34834      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34835      */
34836     monitorResize : true,
34837     /*
34838      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34839      */
34840     toolbar : false,
34841
34842     /**
34843      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34844      * @param {String} id The id of the div to use <b>or create</b>
34845      * @param {String} text The text for the tab
34846      * @param {String} content (optional) Content to put in the TabPanelItem body
34847      * @param {Boolean} closable (optional) True to create a close icon on the tab
34848      * @return {Roo.TabPanelItem} The created TabPanelItem
34849      */
34850     addTab : function(id, text, content, closable)
34851     {
34852         var item = new Roo.bootstrap.panel.TabItem({
34853             panel: this,
34854             id : id,
34855             text : text,
34856             closable : closable
34857         });
34858         this.addTabItem(item);
34859         if(content){
34860             item.setContent(content);
34861         }
34862         return item;
34863     },
34864
34865     /**
34866      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34867      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34868      * @return {Roo.TabPanelItem}
34869      */
34870     getTab : function(id){
34871         return this.items[id];
34872     },
34873
34874     /**
34875      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34876      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34877      */
34878     hideTab : function(id){
34879         var t = this.items[id];
34880         if(!t.isHidden()){
34881            t.setHidden(true);
34882            this.hiddenCount++;
34883            this.autoSizeTabs();
34884         }
34885     },
34886
34887     /**
34888      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34889      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34890      */
34891     unhideTab : function(id){
34892         var t = this.items[id];
34893         if(t.isHidden()){
34894            t.setHidden(false);
34895            this.hiddenCount--;
34896            this.autoSizeTabs();
34897         }
34898     },
34899
34900     /**
34901      * Adds an existing {@link Roo.TabPanelItem}.
34902      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34903      */
34904     addTabItem : function(item){
34905         this.items[item.id] = item;
34906         this.items.push(item);
34907       //  if(this.resizeTabs){
34908     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34909   //         this.autoSizeTabs();
34910 //        }else{
34911 //            item.autoSize();
34912        // }
34913     },
34914
34915     /**
34916      * Removes a {@link Roo.TabPanelItem}.
34917      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34918      */
34919     removeTab : function(id){
34920         var items = this.items;
34921         var tab = items[id];
34922         if(!tab) { return; }
34923         var index = items.indexOf(tab);
34924         if(this.active == tab && items.length > 1){
34925             var newTab = this.getNextAvailable(index);
34926             if(newTab) {
34927                 newTab.activate();
34928             }
34929         }
34930         this.stripEl.dom.removeChild(tab.pnode.dom);
34931         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34932             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34933         }
34934         items.splice(index, 1);
34935         delete this.items[tab.id];
34936         tab.fireEvent("close", tab);
34937         tab.purgeListeners();
34938         this.autoSizeTabs();
34939     },
34940
34941     getNextAvailable : function(start){
34942         var items = this.items;
34943         var index = start;
34944         // look for a next tab that will slide over to
34945         // replace the one being removed
34946         while(index < items.length){
34947             var item = items[++index];
34948             if(item && !item.isHidden()){
34949                 return item;
34950             }
34951         }
34952         // if one isn't found select the previous tab (on the left)
34953         index = start;
34954         while(index >= 0){
34955             var item = items[--index];
34956             if(item && !item.isHidden()){
34957                 return item;
34958             }
34959         }
34960         return null;
34961     },
34962
34963     /**
34964      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34965      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34966      */
34967     disableTab : function(id){
34968         var tab = this.items[id];
34969         if(tab && this.active != tab){
34970             tab.disable();
34971         }
34972     },
34973
34974     /**
34975      * Enables a {@link Roo.TabPanelItem} that is disabled.
34976      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34977      */
34978     enableTab : function(id){
34979         var tab = this.items[id];
34980         tab.enable();
34981     },
34982
34983     /**
34984      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34985      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34986      * @return {Roo.TabPanelItem} The TabPanelItem.
34987      */
34988     activate : function(id){
34989         var tab = this.items[id];
34990         if(!tab){
34991             return null;
34992         }
34993         if(tab == this.active || tab.disabled){
34994             return tab;
34995         }
34996         var e = {};
34997         this.fireEvent("beforetabchange", this, e, tab);
34998         if(e.cancel !== true && !tab.disabled){
34999             if(this.active){
35000                 this.active.hide();
35001             }
35002             this.active = this.items[id];
35003             this.active.show();
35004             this.fireEvent("tabchange", this, this.active);
35005         }
35006         return tab;
35007     },
35008
35009     /**
35010      * Gets the active {@link Roo.TabPanelItem}.
35011      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35012      */
35013     getActiveTab : function(){
35014         return this.active;
35015     },
35016
35017     /**
35018      * Updates the tab body element to fit the height of the container element
35019      * for overflow scrolling
35020      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35021      */
35022     syncHeight : function(targetHeight){
35023         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35024         var bm = this.bodyEl.getMargins();
35025         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35026         this.bodyEl.setHeight(newHeight);
35027         return newHeight;
35028     },
35029
35030     onResize : function(){
35031         if(this.monitorResize){
35032             this.autoSizeTabs();
35033         }
35034     },
35035
35036     /**
35037      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35038      */
35039     beginUpdate : function(){
35040         this.updating = true;
35041     },
35042
35043     /**
35044      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35045      */
35046     endUpdate : function(){
35047         this.updating = false;
35048         this.autoSizeTabs();
35049     },
35050
35051     /**
35052      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35053      */
35054     autoSizeTabs : function(){
35055         var count = this.items.length;
35056         var vcount = count - this.hiddenCount;
35057         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35058             return;
35059         }
35060         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35061         var availWidth = Math.floor(w / vcount);
35062         var b = this.stripBody;
35063         if(b.getWidth() > w){
35064             var tabs = this.items;
35065             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35066             if(availWidth < this.minTabWidth){
35067                 /*if(!this.sleft){    // incomplete scrolling code
35068                     this.createScrollButtons();
35069                 }
35070                 this.showScroll();
35071                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35072             }
35073         }else{
35074             if(this.currentTabWidth < this.preferredTabWidth){
35075                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35076             }
35077         }
35078     },
35079
35080     /**
35081      * Returns the number of tabs in this TabPanel.
35082      * @return {Number}
35083      */
35084      getCount : function(){
35085          return this.items.length;
35086      },
35087
35088     /**
35089      * Resizes all the tabs to the passed width
35090      * @param {Number} The new width
35091      */
35092     setTabWidth : function(width){
35093         this.currentTabWidth = width;
35094         for(var i = 0, len = this.items.length; i < len; i++) {
35095                 if(!this.items[i].isHidden()) {
35096                 this.items[i].setWidth(width);
35097             }
35098         }
35099     },
35100
35101     /**
35102      * Destroys this TabPanel
35103      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35104      */
35105     destroy : function(removeEl){
35106         Roo.EventManager.removeResizeListener(this.onResize, this);
35107         for(var i = 0, len = this.items.length; i < len; i++){
35108             this.items[i].purgeListeners();
35109         }
35110         if(removeEl === true){
35111             this.el.update("");
35112             this.el.remove();
35113         }
35114     },
35115     
35116     createStrip : function(container)
35117     {
35118         var strip = document.createElement("nav");
35119         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35120         container.appendChild(strip);
35121         return strip;
35122     },
35123     
35124     createStripList : function(strip)
35125     {
35126         // div wrapper for retard IE
35127         // returns the "tr" element.
35128         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35129         //'<div class="x-tabs-strip-wrap">'+
35130           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35131           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35132         return strip.firstChild; //.firstChild.firstChild.firstChild;
35133     },
35134     createBody : function(container)
35135     {
35136         var body = document.createElement("div");
35137         Roo.id(body, "tab-body");
35138         //Roo.fly(body).addClass("x-tabs-body");
35139         Roo.fly(body).addClass("tab-content");
35140         container.appendChild(body);
35141         return body;
35142     },
35143     createItemBody :function(bodyEl, id){
35144         var body = Roo.getDom(id);
35145         if(!body){
35146             body = document.createElement("div");
35147             body.id = id;
35148         }
35149         //Roo.fly(body).addClass("x-tabs-item-body");
35150         Roo.fly(body).addClass("tab-pane");
35151          bodyEl.insertBefore(body, bodyEl.firstChild);
35152         return body;
35153     },
35154     /** @private */
35155     createStripElements :  function(stripEl, text, closable)
35156     {
35157         var td = document.createElement("li"); // was td..
35158         
35159         
35160         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35161         
35162         
35163         stripEl.appendChild(td);
35164         /*if(closable){
35165             td.className = "x-tabs-closable";
35166             if(!this.closeTpl){
35167                 this.closeTpl = new Roo.Template(
35168                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35169                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35170                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35171                 );
35172             }
35173             var el = this.closeTpl.overwrite(td, {"text": text});
35174             var close = el.getElementsByTagName("div")[0];
35175             var inner = el.getElementsByTagName("em")[0];
35176             return {"el": el, "close": close, "inner": inner};
35177         } else {
35178         */
35179         // not sure what this is..
35180             if(!this.tabTpl){
35181                 //this.tabTpl = new Roo.Template(
35182                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35183                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35184                 //);
35185                 this.tabTpl = new Roo.Template(
35186                    '<a href="#">' +
35187                    '<span unselectable="on"' +
35188                             (this.disableTooltips ? '' : ' title="{text}"') +
35189                             ' >{text}</span></span></a>'
35190                 );
35191                 
35192             }
35193             var el = this.tabTpl.overwrite(td, {"text": text});
35194             var inner = el.getElementsByTagName("span")[0];
35195             return {"el": el, "inner": inner};
35196         //}
35197     }
35198         
35199     
35200 });
35201
35202 /**
35203  * @class Roo.TabPanelItem
35204  * @extends Roo.util.Observable
35205  * Represents an individual item (tab plus body) in a TabPanel.
35206  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35207  * @param {String} id The id of this TabPanelItem
35208  * @param {String} text The text for the tab of this TabPanelItem
35209  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35210  */
35211 Roo.bootstrap.panel.TabItem = function(config){
35212     /**
35213      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35214      * @type Roo.TabPanel
35215      */
35216     this.tabPanel = config.panel;
35217     /**
35218      * The id for this TabPanelItem
35219      * @type String
35220      */
35221     this.id = config.id;
35222     /** @private */
35223     this.disabled = false;
35224     /** @private */
35225     this.text = config.text;
35226     /** @private */
35227     this.loaded = false;
35228     this.closable = config.closable;
35229
35230     /**
35231      * The body element for this TabPanelItem.
35232      * @type Roo.Element
35233      */
35234     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35235     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35236     this.bodyEl.setStyle("display", "block");
35237     this.bodyEl.setStyle("zoom", "1");
35238     //this.hideAction();
35239
35240     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35241     /** @private */
35242     this.el = Roo.get(els.el);
35243     this.inner = Roo.get(els.inner, true);
35244     this.textEl = Roo.get(this.el.dom.firstChild, true);
35245     this.pnode = Roo.get(els.el.parentNode, true);
35246     this.el.on("mousedown", this.onTabMouseDown, this);
35247     this.el.on("click", this.onTabClick, this);
35248     /** @private */
35249     if(config.closable){
35250         var c = Roo.get(els.close, true);
35251         c.dom.title = this.closeText;
35252         c.addClassOnOver("close-over");
35253         c.on("click", this.closeClick, this);
35254      }
35255
35256     this.addEvents({
35257          /**
35258          * @event activate
35259          * Fires when this tab becomes the active tab.
35260          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35261          * @param {Roo.TabPanelItem} this
35262          */
35263         "activate": true,
35264         /**
35265          * @event beforeclose
35266          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35267          * @param {Roo.TabPanelItem} this
35268          * @param {Object} e Set cancel to true on this object to cancel the close.
35269          */
35270         "beforeclose": true,
35271         /**
35272          * @event close
35273          * Fires when this tab is closed.
35274          * @param {Roo.TabPanelItem} this
35275          */
35276          "close": true,
35277         /**
35278          * @event deactivate
35279          * Fires when this tab is no longer the active tab.
35280          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35281          * @param {Roo.TabPanelItem} this
35282          */
35283          "deactivate" : true
35284     });
35285     this.hidden = false;
35286
35287     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35288 };
35289
35290 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35291            {
35292     purgeListeners : function(){
35293        Roo.util.Observable.prototype.purgeListeners.call(this);
35294        this.el.removeAllListeners();
35295     },
35296     /**
35297      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35298      */
35299     show : function(){
35300         this.pnode.addClass("active");
35301         this.showAction();
35302         if(Roo.isOpera){
35303             this.tabPanel.stripWrap.repaint();
35304         }
35305         this.fireEvent("activate", this.tabPanel, this);
35306     },
35307
35308     /**
35309      * Returns true if this tab is the active tab.
35310      * @return {Boolean}
35311      */
35312     isActive : function(){
35313         return this.tabPanel.getActiveTab() == this;
35314     },
35315
35316     /**
35317      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35318      */
35319     hide : function(){
35320         this.pnode.removeClass("active");
35321         this.hideAction();
35322         this.fireEvent("deactivate", this.tabPanel, this);
35323     },
35324
35325     hideAction : function(){
35326         this.bodyEl.hide();
35327         this.bodyEl.setStyle("position", "absolute");
35328         this.bodyEl.setLeft("-20000px");
35329         this.bodyEl.setTop("-20000px");
35330     },
35331
35332     showAction : function(){
35333         this.bodyEl.setStyle("position", "relative");
35334         this.bodyEl.setTop("");
35335         this.bodyEl.setLeft("");
35336         this.bodyEl.show();
35337     },
35338
35339     /**
35340      * Set the tooltip for the tab.
35341      * @param {String} tooltip The tab's tooltip
35342      */
35343     setTooltip : function(text){
35344         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35345             this.textEl.dom.qtip = text;
35346             this.textEl.dom.removeAttribute('title');
35347         }else{
35348             this.textEl.dom.title = text;
35349         }
35350     },
35351
35352     onTabClick : function(e){
35353         e.preventDefault();
35354         this.tabPanel.activate(this.id);
35355     },
35356
35357     onTabMouseDown : function(e){
35358         e.preventDefault();
35359         this.tabPanel.activate(this.id);
35360     },
35361 /*
35362     getWidth : function(){
35363         return this.inner.getWidth();
35364     },
35365
35366     setWidth : function(width){
35367         var iwidth = width - this.pnode.getPadding("lr");
35368         this.inner.setWidth(iwidth);
35369         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35370         this.pnode.setWidth(width);
35371     },
35372 */
35373     /**
35374      * Show or hide the tab
35375      * @param {Boolean} hidden True to hide or false to show.
35376      */
35377     setHidden : function(hidden){
35378         this.hidden = hidden;
35379         this.pnode.setStyle("display", hidden ? "none" : "");
35380     },
35381
35382     /**
35383      * Returns true if this tab is "hidden"
35384      * @return {Boolean}
35385      */
35386     isHidden : function(){
35387         return this.hidden;
35388     },
35389
35390     /**
35391      * Returns the text for this tab
35392      * @return {String}
35393      */
35394     getText : function(){
35395         return this.text;
35396     },
35397     /*
35398     autoSize : function(){
35399         //this.el.beginMeasure();
35400         this.textEl.setWidth(1);
35401         /*
35402          *  #2804 [new] Tabs in Roojs
35403          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35404          */
35405         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35406         //this.el.endMeasure();
35407     //},
35408
35409     /**
35410      * Sets the text for the tab (Note: this also sets the tooltip text)
35411      * @param {String} text The tab's text and tooltip
35412      */
35413     setText : function(text){
35414         this.text = text;
35415         this.textEl.update(text);
35416         this.setTooltip(text);
35417         //if(!this.tabPanel.resizeTabs){
35418         //    this.autoSize();
35419         //}
35420     },
35421     /**
35422      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35423      */
35424     activate : function(){
35425         this.tabPanel.activate(this.id);
35426     },
35427
35428     /**
35429      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35430      */
35431     disable : function(){
35432         if(this.tabPanel.active != this){
35433             this.disabled = true;
35434             this.pnode.addClass("disabled");
35435         }
35436     },
35437
35438     /**
35439      * Enables this TabPanelItem if it was previously disabled.
35440      */
35441     enable : function(){
35442         this.disabled = false;
35443         this.pnode.removeClass("disabled");
35444     },
35445
35446     /**
35447      * Sets the content for this TabPanelItem.
35448      * @param {String} content The content
35449      * @param {Boolean} loadScripts true to look for and load scripts
35450      */
35451     setContent : function(content, loadScripts){
35452         this.bodyEl.update(content, loadScripts);
35453     },
35454
35455     /**
35456      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35457      * @return {Roo.UpdateManager} The UpdateManager
35458      */
35459     getUpdateManager : function(){
35460         return this.bodyEl.getUpdateManager();
35461     },
35462
35463     /**
35464      * Set a URL to be used to load the content for this TabPanelItem.
35465      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35466      * @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)
35467      * @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)
35468      * @return {Roo.UpdateManager} The UpdateManager
35469      */
35470     setUrl : function(url, params, loadOnce){
35471         if(this.refreshDelegate){
35472             this.un('activate', this.refreshDelegate);
35473         }
35474         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35475         this.on("activate", this.refreshDelegate);
35476         return this.bodyEl.getUpdateManager();
35477     },
35478
35479     /** @private */
35480     _handleRefresh : function(url, params, loadOnce){
35481         if(!loadOnce || !this.loaded){
35482             var updater = this.bodyEl.getUpdateManager();
35483             updater.update(url, params, this._setLoaded.createDelegate(this));
35484         }
35485     },
35486
35487     /**
35488      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35489      *   Will fail silently if the setUrl method has not been called.
35490      *   This does not activate the panel, just updates its content.
35491      */
35492     refresh : function(){
35493         if(this.refreshDelegate){
35494            this.loaded = false;
35495            this.refreshDelegate();
35496         }
35497     },
35498
35499     /** @private */
35500     _setLoaded : function(){
35501         this.loaded = true;
35502     },
35503
35504     /** @private */
35505     closeClick : function(e){
35506         var o = {};
35507         e.stopEvent();
35508         this.fireEvent("beforeclose", this, o);
35509         if(o.cancel !== true){
35510             this.tabPanel.removeTab(this.id);
35511         }
35512     },
35513     /**
35514      * The text displayed in the tooltip for the close icon.
35515      * @type String
35516      */
35517     closeText : "Close this tab"
35518 });