roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default false
2460  * @cfg {String} size (sm|lg) default empty
2461  * 
2462  * 
2463  * @constructor
2464  * Create a new Modal Dialog
2465  * @param {Object} config The config object
2466  */
2467
2468 Roo.bootstrap.Modal = function(config){
2469     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470     this.addEvents({
2471         // raw events
2472         /**
2473          * @event btnclick
2474          * The raw btnclick event for the button
2475          * @param {Roo.EventObject} e
2476          */
2477         "btnclick" : true
2478     });
2479     this.buttons = this.buttons || [];
2480      
2481     if (this.tmpl) {
2482         this.tmpl = Roo.factory(this.tmpl);
2483     }
2484     
2485 };
2486
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2488     
2489     title : 'test dialog',
2490    
2491     buttons : false,
2492     
2493     // set on load...
2494      
2495     html: false,
2496     
2497     tmp: false,
2498     
2499     specificTitle: false,
2500     
2501     buttonPosition: 'right',
2502     
2503     allow_close : true,
2504     
2505     animate : true,
2506     
2507     fitwindow: false,
2508     
2509     
2510      // private
2511     dialogEl: false,
2512     bodyEl:  false,
2513     footerEl:  false,
2514     titleEl:  false,
2515     closeEl:  false,
2516     
2517     size: '',
2518     
2519     
2520     onRender : function(ct, position)
2521     {
2522         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2523      
2524         if(!this.el){
2525             var cfg = Roo.apply({},  this.getAutoCreate());
2526             cfg.id = Roo.id();
2527             //if(!cfg.name){
2528             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2529             //}
2530             //if (!cfg.name.length) {
2531             //    delete cfg.name;
2532            // }
2533             if (this.cls) {
2534                 cfg.cls += ' ' + this.cls;
2535             }
2536             if (this.style) {
2537                 cfg.style = this.style;
2538             }
2539             this.el = Roo.get(document.body).createChild(cfg, position);
2540         }
2541         //var type = this.el.dom.type;
2542         
2543         
2544         if(this.tabIndex !== undefined){
2545             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2546         }
2547         
2548         this.dialogEl = this.el.select('.modal-dialog',true).first();
2549         this.bodyEl = this.el.select('.modal-body',true).first();
2550         this.closeEl = this.el.select('.modal-header .close', true).first();
2551         this.footerEl = this.el.select('.modal-footer',true).first();
2552         this.titleEl = this.el.select('.modal-title',true).first();
2553         
2554         
2555          
2556         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557         this.maskEl.enableDisplayMode("block");
2558         this.maskEl.hide();
2559         //this.el.addClass("x-dlg-modal");
2560     
2561         if (this.buttons.length) {
2562             Roo.each(this.buttons, function(bb) {
2563                 var b = Roo.apply({}, bb);
2564                 b.xns = b.xns || Roo.bootstrap;
2565                 b.xtype = b.xtype || 'Button';
2566                 if (typeof(b.listeners) == 'undefined') {
2567                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2568                 }
2569                 
2570                 var btn = Roo.factory(b);
2571                 
2572                 btn.render(this.el.select('.modal-footer div').first());
2573                 
2574             },this);
2575         }
2576         // render the children.
2577         var nitems = [];
2578         
2579         if(typeof(this.items) != 'undefined'){
2580             var items = this.items;
2581             delete this.items;
2582
2583             for(var i =0;i < items.length;i++) {
2584                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2585             }
2586         }
2587         
2588         this.items = nitems;
2589         
2590         // where are these used - they used to be body/close/footer
2591         
2592        
2593         this.initEvents();
2594         //this.el.addClass([this.fieldClass, this.cls]);
2595         
2596     },
2597     
2598     getAutoCreate : function(){
2599         
2600         
2601         var bdy = {
2602                 cls : 'modal-body',
2603                 html : this.html || ''
2604         };
2605         
2606         var title = {
2607             tag: 'h4',
2608             cls : 'modal-title',
2609             html : this.title
2610         };
2611         
2612         if(this.specificTitle){
2613             title = this.title;
2614             
2615         };
2616         
2617         var header = [];
2618         if (this.allow_close) {
2619             header.push({
2620                 tag: 'button',
2621                 cls : 'close',
2622                 html : '&times'
2623             });
2624         }
2625         
2626         header.push(title);
2627         
2628         var size = '';
2629         
2630         if(this.size.length){
2631             size = 'modal-' + this.size;
2632         }
2633         
2634         var modal = {
2635             cls: "modal",
2636             style : 'display: none',
2637             cn : [
2638                 {
2639                     cls: "modal-dialog " + size,
2640                     cn : [
2641                         {
2642                             cls : "modal-content",
2643                             cn : [
2644                                 {
2645                                     cls : 'modal-header',
2646                                     cn : header
2647                                 },
2648                                 bdy,
2649                                 {
2650                                     cls : 'modal-footer',
2651                                     cn : [
2652                                         {
2653                                             tag: 'div',
2654                                             cls: 'btn-' + this.buttonPosition
2655                                         }
2656                                     ]
2657                                     
2658                                 }
2659                                 
2660                                 
2661                             ]
2662                             
2663                         }
2664                     ]
2665                         
2666                 }
2667             ]
2668         };
2669         
2670         if(this.animate){
2671             modal.cls += ' fade';
2672         }
2673         
2674         return modal;
2675           
2676     },
2677     getChildContainer : function() {
2678          
2679          return this.bodyEl;
2680         
2681     },
2682     getButtonContainer : function() {
2683          return this.el.select('.modal-footer div',true).first();
2684         
2685     },
2686     initEvents : function()
2687     {
2688         if (this.allow_close) {
2689             this.closeEl.on('click', this.hide, this);
2690         }
2691         Roo.EventManager.onWindowResize(this.resize, this, true);
2692         
2693  
2694     },
2695     
2696     resize : function()
2697     {
2698         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2699         if (this.fitwindow) {
2700             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2702             this.setSize(w,h);
2703         }
2704     },
2705     
2706     setSize : function(w,h)
2707     {
2708         if (!w && !h) {
2709             return;
2710         }
2711         this.resizeTo(w,h);
2712     },
2713     
2714     show : function() {
2715         
2716         if (!this.rendered) {
2717             this.render();
2718         }
2719         
2720         this.el.setStyle('display', 'block');
2721         
2722         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2723             var _this = this;
2724             (function(){
2725                 this.el.addClass('in');
2726             }).defer(50, this);
2727         }else{
2728             this.el.addClass('in');
2729             
2730         }
2731         
2732         // not sure how we can show data in here.. 
2733         //if (this.tmpl) {
2734         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2735         //}
2736         
2737         Roo.get(document.body).addClass("x-body-masked");
2738         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2739         this.maskEl.show();
2740         this.el.setStyle('zIndex', '10001');
2741        
2742         this.fireEvent('show', this);
2743         
2744         this.resize();
2745         
2746         (function () {
2747             this.items.forEach( function(e) {
2748                 e.layout ? e.layout() : false;
2749                     
2750             });
2751         }).defer(100,this);
2752         
2753     },
2754     hide : function()
2755     {
2756         this.maskEl.hide();
2757         Roo.get(document.body).removeClass("x-body-masked");
2758         this.el.removeClass('in');
2759         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2760         
2761         if(this.animate){ // why
2762             var _this = this;
2763             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2764         }else{
2765             this.el.setStyle('display', 'none');
2766         }
2767         
2768         this.fireEvent('hide', this);
2769     },
2770     
2771     addButton : function(str, cb)
2772     {
2773          
2774         
2775         var b = Roo.apply({}, { html : str } );
2776         b.xns = b.xns || Roo.bootstrap;
2777         b.xtype = b.xtype || 'Button';
2778         if (typeof(b.listeners) == 'undefined') {
2779             b.listeners = { click : cb.createDelegate(this)  };
2780         }
2781         
2782         var btn = Roo.factory(b);
2783            
2784         btn.render(this.el.select('.modal-footer div').first());
2785         
2786         return btn;   
2787        
2788     },
2789     
2790     setDefaultButton : function(btn)
2791     {
2792         //this.el.select('.modal-footer').()
2793     },
2794     diff : false,
2795     
2796     resizeTo: function(w,h)
2797     {
2798         // skip.. ?? why??
2799         
2800         this.dialogEl.setWidth(w);
2801         if (this.diff === false) {
2802             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2803         }
2804         
2805         this.bodyEl.setHeight(h-this.diff);
2806         
2807         
2808     },
2809     setContentSize  : function(w, h)
2810     {
2811         
2812     },
2813     onButtonClick: function(btn,e)
2814     {
2815         //Roo.log([a,b,c]);
2816         this.fireEvent('btnclick', btn.name, e);
2817     },
2818      /**
2819      * Set the title of the Dialog
2820      * @param {String} str new Title
2821      */
2822     setTitle: function(str) {
2823         this.titleEl.dom.innerHTML = str;    
2824     },
2825     /**
2826      * Set the body of the Dialog
2827      * @param {String} str new Title
2828      */
2829     setBody: function(str) {
2830         this.bodyEl.dom.innerHTML = str;    
2831     },
2832     /**
2833      * Set the body of the Dialog using the template
2834      * @param {Obj} data - apply this data to the template and replace the body contents.
2835      */
2836     applyBody: function(obj)
2837     {
2838         if (!this.tmpl) {
2839             Roo.log("Error - using apply Body without a template");
2840             //code
2841         }
2842         this.tmpl.overwrite(this.bodyEl, obj);
2843     }
2844     
2845 });
2846
2847
2848 Roo.apply(Roo.bootstrap.Modal,  {
2849     /**
2850          * Button config that displays a single OK button
2851          * @type Object
2852          */
2853         OK :  [{
2854             name : 'ok',
2855             weight : 'primary',
2856             html : 'OK'
2857         }], 
2858         /**
2859          * Button config that displays Yes and No buttons
2860          * @type Object
2861          */
2862         YESNO : [
2863             {
2864                 name  : 'no',
2865                 html : 'No'
2866             },
2867             {
2868                 name  :'yes',
2869                 weight : 'primary',
2870                 html : 'Yes'
2871             }
2872         ],
2873         
2874         /**
2875          * Button config that displays OK and Cancel buttons
2876          * @type Object
2877          */
2878         OKCANCEL : [
2879             {
2880                name : 'cancel',
2881                 html : 'Cancel'
2882             },
2883             {
2884                 name : 'ok',
2885                 weight : 'primary',
2886                 html : 'OK'
2887             }
2888         ],
2889         /**
2890          * Button config that displays Yes, No and Cancel buttons
2891          * @type Object
2892          */
2893         YESNOCANCEL : [
2894             {
2895                 name : 'yes',
2896                 weight : 'primary',
2897                 html : 'Yes'
2898             },
2899             {
2900                 name : 'no',
2901                 html : 'No'
2902             },
2903             {
2904                 name : 'cancel',
2905                 html : 'Cancel'
2906             }
2907         ]
2908 });
2909  
2910  /*
2911  * - LGPL
2912  *
2913  * messagebox - can be used as a replace
2914  * 
2915  */
2916 /**
2917  * @class Roo.MessageBox
2918  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2919  * Example usage:
2920  *<pre><code>
2921 // Basic alert:
2922 Roo.Msg.alert('Status', 'Changes saved successfully.');
2923
2924 // Prompt for user data:
2925 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2926     if (btn == 'ok'){
2927         // process text value...
2928     }
2929 });
2930
2931 // Show a dialog using config options:
2932 Roo.Msg.show({
2933    title:'Save Changes?',
2934    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2935    buttons: Roo.Msg.YESNOCANCEL,
2936    fn: processResult,
2937    animEl: 'elId'
2938 });
2939 </code></pre>
2940  * @singleton
2941  */
2942 Roo.bootstrap.MessageBox = function(){
2943     var dlg, opt, mask, waitTimer;
2944     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2945     var buttons, activeTextEl, bwidth;
2946
2947     
2948     // private
2949     var handleButton = function(button){
2950         dlg.hide();
2951         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2952     };
2953
2954     // private
2955     var handleHide = function(){
2956         if(opt && opt.cls){
2957             dlg.el.removeClass(opt.cls);
2958         }
2959         //if(waitTimer){
2960         //    Roo.TaskMgr.stop(waitTimer);
2961         //    waitTimer = null;
2962         //}
2963     };
2964
2965     // private
2966     var updateButtons = function(b){
2967         var width = 0;
2968         if(!b){
2969             buttons["ok"].hide();
2970             buttons["cancel"].hide();
2971             buttons["yes"].hide();
2972             buttons["no"].hide();
2973             //dlg.footer.dom.style.display = 'none';
2974             return width;
2975         }
2976         dlg.footerEl.dom.style.display = '';
2977         for(var k in buttons){
2978             if(typeof buttons[k] != "function"){
2979                 if(b[k]){
2980                     buttons[k].show();
2981                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2982                     width += buttons[k].el.getWidth()+15;
2983                 }else{
2984                     buttons[k].hide();
2985                 }
2986             }
2987         }
2988         return width;
2989     };
2990
2991     // private
2992     var handleEsc = function(d, k, e){
2993         if(opt && opt.closable !== false){
2994             dlg.hide();
2995         }
2996         if(e){
2997             e.stopEvent();
2998         }
2999     };
3000
3001     return {
3002         /**
3003          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3004          * @return {Roo.BasicDialog} The BasicDialog element
3005          */
3006         getDialog : function(){
3007            if(!dlg){
3008                 dlg = new Roo.bootstrap.Modal( {
3009                     //draggable: true,
3010                     //resizable:false,
3011                     //constraintoviewport:false,
3012                     //fixedcenter:true,
3013                     //collapsible : false,
3014                     //shim:true,
3015                     //modal: true,
3016                   //  width:400,
3017                   //  height:100,
3018                     //buttonAlign:"center",
3019                     closeClick : function(){
3020                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3021                             handleButton("no");
3022                         }else{
3023                             handleButton("cancel");
3024                         }
3025                     }
3026                 });
3027                 dlg.render();
3028                 dlg.on("hide", handleHide);
3029                 mask = dlg.mask;
3030                 //dlg.addKeyListener(27, handleEsc);
3031                 buttons = {};
3032                 this.buttons = buttons;
3033                 var bt = this.buttonText;
3034                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3035                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3036                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3037                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3038                 //Roo.log(buttons);
3039                 bodyEl = dlg.bodyEl.createChild({
3040
3041                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3042                         '<textarea class="roo-mb-textarea"></textarea>' +
3043                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3044                 });
3045                 msgEl = bodyEl.dom.firstChild;
3046                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3047                 textboxEl.enableDisplayMode();
3048                 textboxEl.addKeyListener([10,13], function(){
3049                     if(dlg.isVisible() && opt && opt.buttons){
3050                         if(opt.buttons.ok){
3051                             handleButton("ok");
3052                         }else if(opt.buttons.yes){
3053                             handleButton("yes");
3054                         }
3055                     }
3056                 });
3057                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3058                 textareaEl.enableDisplayMode();
3059                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3060                 progressEl.enableDisplayMode();
3061                 var pf = progressEl.dom.firstChild;
3062                 if (pf) {
3063                     pp = Roo.get(pf.firstChild);
3064                     pp.setHeight(pf.offsetHeight);
3065                 }
3066                 
3067             }
3068             return dlg;
3069         },
3070
3071         /**
3072          * Updates the message box body text
3073          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3074          * the XHTML-compliant non-breaking space character '&amp;#160;')
3075          * @return {Roo.MessageBox} This message box
3076          */
3077         updateText : function(text){
3078             if(!dlg.isVisible() && !opt.width){
3079                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3080             }
3081             msgEl.innerHTML = text || '&#160;';
3082       
3083             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3084             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3085             var w = Math.max(
3086                     Math.min(opt.width || cw , this.maxWidth), 
3087                     Math.max(opt.minWidth || this.minWidth, bwidth)
3088             );
3089             if(opt.prompt){
3090                 activeTextEl.setWidth(w);
3091             }
3092             if(dlg.isVisible()){
3093                 dlg.fixedcenter = false;
3094             }
3095             // to big, make it scroll. = But as usual stupid IE does not support
3096             // !important..
3097             
3098             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3099                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3100                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3101             } else {
3102                 bodyEl.dom.style.height = '';
3103                 bodyEl.dom.style.overflowY = '';
3104             }
3105             if (cw > w) {
3106                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3107             } else {
3108                 bodyEl.dom.style.overflowX = '';
3109             }
3110             
3111             dlg.setContentSize(w, bodyEl.getHeight());
3112             if(dlg.isVisible()){
3113                 dlg.fixedcenter = true;
3114             }
3115             return this;
3116         },
3117
3118         /**
3119          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3120          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3121          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3122          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3123          * @return {Roo.MessageBox} This message box
3124          */
3125         updateProgress : function(value, text){
3126             if(text){
3127                 this.updateText(text);
3128             }
3129             if (pp) { // weird bug on my firefox - for some reason this is not defined
3130                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3131             }
3132             return this;
3133         },        
3134
3135         /**
3136          * Returns true if the message box is currently displayed
3137          * @return {Boolean} True if the message box is visible, else false
3138          */
3139         isVisible : function(){
3140             return dlg && dlg.isVisible();  
3141         },
3142
3143         /**
3144          * Hides the message box if it is displayed
3145          */
3146         hide : function(){
3147             if(this.isVisible()){
3148                 dlg.hide();
3149             }  
3150         },
3151
3152         /**
3153          * Displays a new message box, or reinitializes an existing message box, based on the config options
3154          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3155          * The following config object properties are supported:
3156          * <pre>
3157 Property    Type             Description
3158 ----------  ---------------  ------------------------------------------------------------------------------------
3159 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3160                                    closes (defaults to undefined)
3161 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3162                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3163 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3164                                    progress and wait dialogs will ignore this property and always hide the
3165                                    close button as they can only be closed programmatically.
3166 cls               String           A custom CSS class to apply to the message box element
3167 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3168                                    displayed (defaults to 75)
3169 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3170                                    function will be btn (the name of the button that was clicked, if applicable,
3171                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3172                                    Progress and wait dialogs will ignore this option since they do not respond to
3173                                    user actions and can only be closed programmatically, so any required function
3174                                    should be called by the same code after it closes the dialog.
3175 icon              String           A CSS class that provides a background image to be used as an icon for
3176                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3177 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3178 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3179 modal             Boolean          False to allow user interaction with the page while the message box is
3180                                    displayed (defaults to true)
3181 msg               String           A string that will replace the existing message box body text (defaults
3182                                    to the XHTML-compliant non-breaking space character '&#160;')
3183 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3184 progress          Boolean          True to display a progress bar (defaults to false)
3185 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3186 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3187 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3188 title             String           The title text
3189 value             String           The string value to set into the active textbox element if displayed
3190 wait              Boolean          True to display a progress bar (defaults to false)
3191 width             Number           The width of the dialog in pixels
3192 </pre>
3193          *
3194          * Example usage:
3195          * <pre><code>
3196 Roo.Msg.show({
3197    title: 'Address',
3198    msg: 'Please enter your address:',
3199    width: 300,
3200    buttons: Roo.MessageBox.OKCANCEL,
3201    multiline: true,
3202    fn: saveAddress,
3203    animEl: 'addAddressBtn'
3204 });
3205 </code></pre>
3206          * @param {Object} config Configuration options
3207          * @return {Roo.MessageBox} This message box
3208          */
3209         show : function(options)
3210         {
3211             
3212             // this causes nightmares if you show one dialog after another
3213             // especially on callbacks..
3214              
3215             if(this.isVisible()){
3216                 
3217                 this.hide();
3218                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3219                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3220                 Roo.log("New Dialog Message:" +  options.msg )
3221                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3222                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3223                 
3224             }
3225             var d = this.getDialog();
3226             opt = options;
3227             d.setTitle(opt.title || "&#160;");
3228             d.closeEl.setDisplayed(opt.closable !== false);
3229             activeTextEl = textboxEl;
3230             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3231             if(opt.prompt){
3232                 if(opt.multiline){
3233                     textboxEl.hide();
3234                     textareaEl.show();
3235                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3236                         opt.multiline : this.defaultTextHeight);
3237                     activeTextEl = textareaEl;
3238                 }else{
3239                     textboxEl.show();
3240                     textareaEl.hide();
3241                 }
3242             }else{
3243                 textboxEl.hide();
3244                 textareaEl.hide();
3245             }
3246             progressEl.setDisplayed(opt.progress === true);
3247             this.updateProgress(0);
3248             activeTextEl.dom.value = opt.value || "";
3249             if(opt.prompt){
3250                 dlg.setDefaultButton(activeTextEl);
3251             }else{
3252                 var bs = opt.buttons;
3253                 var db = null;
3254                 if(bs && bs.ok){
3255                     db = buttons["ok"];
3256                 }else if(bs && bs.yes){
3257                     db = buttons["yes"];
3258                 }
3259                 dlg.setDefaultButton(db);
3260             }
3261             bwidth = updateButtons(opt.buttons);
3262             this.updateText(opt.msg);
3263             if(opt.cls){
3264                 d.el.addClass(opt.cls);
3265             }
3266             d.proxyDrag = opt.proxyDrag === true;
3267             d.modal = opt.modal !== false;
3268             d.mask = opt.modal !== false ? mask : false;
3269             if(!d.isVisible()){
3270                 // force it to the end of the z-index stack so it gets a cursor in FF
3271                 document.body.appendChild(dlg.el.dom);
3272                 d.animateTarget = null;
3273                 d.show(options.animEl);
3274             }
3275             return this;
3276         },
3277
3278         /**
3279          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3280          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3281          * and closing the message box when the process is complete.
3282          * @param {String} title The title bar text
3283          * @param {String} msg The message box body text
3284          * @return {Roo.MessageBox} This message box
3285          */
3286         progress : function(title, msg){
3287             this.show({
3288                 title : title,
3289                 msg : msg,
3290                 buttons: false,
3291                 progress:true,
3292                 closable:false,
3293                 minWidth: this.minProgressWidth,
3294                 modal : true
3295             });
3296             return this;
3297         },
3298
3299         /**
3300          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3301          * If a callback function is passed it will be called after the user clicks the button, and the
3302          * id of the button that was clicked will be passed as the only parameter to the callback
3303          * (could also be the top-right close button).
3304          * @param {String} title The title bar text
3305          * @param {String} msg The message box body text
3306          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3307          * @param {Object} scope (optional) The scope of the callback function
3308          * @return {Roo.MessageBox} This message box
3309          */
3310         alert : function(title, msg, fn, scope){
3311             this.show({
3312                 title : title,
3313                 msg : msg,
3314                 buttons: this.OK,
3315                 fn: fn,
3316                 scope : scope,
3317                 modal : true
3318             });
3319             return this;
3320         },
3321
3322         /**
3323          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3324          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3325          * You are responsible for closing the message box when the process is complete.
3326          * @param {String} msg The message box body text
3327          * @param {String} title (optional) The title bar text
3328          * @return {Roo.MessageBox} This message box
3329          */
3330         wait : function(msg, title){
3331             this.show({
3332                 title : title,
3333                 msg : msg,
3334                 buttons: false,
3335                 closable:false,
3336                 progress:true,
3337                 modal:true,
3338                 width:300,
3339                 wait:true
3340             });
3341             waitTimer = Roo.TaskMgr.start({
3342                 run: function(i){
3343                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3344                 },
3345                 interval: 1000
3346             });
3347             return this;
3348         },
3349
3350         /**
3351          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3352          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3353          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3354          * @param {String} title The title bar text
3355          * @param {String} msg The message box body text
3356          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3357          * @param {Object} scope (optional) The scope of the callback function
3358          * @return {Roo.MessageBox} This message box
3359          */
3360         confirm : function(title, msg, fn, scope){
3361             this.show({
3362                 title : title,
3363                 msg : msg,
3364                 buttons: this.YESNO,
3365                 fn: fn,
3366                 scope : scope,
3367                 modal : true
3368             });
3369             return this;
3370         },
3371
3372         /**
3373          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3374          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3375          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3376          * (could also be the top-right close button) and the text that was entered will be passed as the two
3377          * parameters to the callback.
3378          * @param {String} title The title bar text
3379          * @param {String} msg The message box body text
3380          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3381          * @param {Object} scope (optional) The scope of the callback function
3382          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3383          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3384          * @return {Roo.MessageBox} This message box
3385          */
3386         prompt : function(title, msg, fn, scope, multiline){
3387             this.show({
3388                 title : title,
3389                 msg : msg,
3390                 buttons: this.OKCANCEL,
3391                 fn: fn,
3392                 minWidth:250,
3393                 scope : scope,
3394                 prompt:true,
3395                 multiline: multiline,
3396                 modal : true
3397             });
3398             return this;
3399         },
3400
3401         /**
3402          * Button config that displays a single OK button
3403          * @type Object
3404          */
3405         OK : {ok:true},
3406         /**
3407          * Button config that displays Yes and No buttons
3408          * @type Object
3409          */
3410         YESNO : {yes:true, no:true},
3411         /**
3412          * Button config that displays OK and Cancel buttons
3413          * @type Object
3414          */
3415         OKCANCEL : {ok:true, cancel:true},
3416         /**
3417          * Button config that displays Yes, No and Cancel buttons
3418          * @type Object
3419          */
3420         YESNOCANCEL : {yes:true, no:true, cancel:true},
3421
3422         /**
3423          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3424          * @type Number
3425          */
3426         defaultTextHeight : 75,
3427         /**
3428          * The maximum width in pixels of the message box (defaults to 600)
3429          * @type Number
3430          */
3431         maxWidth : 600,
3432         /**
3433          * The minimum width in pixels of the message box (defaults to 100)
3434          * @type Number
3435          */
3436         minWidth : 100,
3437         /**
3438          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3439          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3440          * @type Number
3441          */
3442         minProgressWidth : 250,
3443         /**
3444          * An object containing the default button text strings that can be overriden for localized language support.
3445          * Supported properties are: ok, cancel, yes and no.
3446          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3447          * @type Object
3448          */
3449         buttonText : {
3450             ok : "OK",
3451             cancel : "Cancel",
3452             yes : "Yes",
3453             no : "No"
3454         }
3455     };
3456 }();
3457
3458 /**
3459  * Shorthand for {@link Roo.MessageBox}
3460  */
3461 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3462 Roo.Msg = Roo.Msg || Roo.MessageBox;
3463 /*
3464  * - LGPL
3465  *
3466  * navbar
3467  * 
3468  */
3469
3470 /**
3471  * @class Roo.bootstrap.Navbar
3472  * @extends Roo.bootstrap.Component
3473  * Bootstrap Navbar class
3474
3475  * @constructor
3476  * Create a new Navbar
3477  * @param {Object} config The config object
3478  */
3479
3480
3481 Roo.bootstrap.Navbar = function(config){
3482     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3483     this.addEvents({
3484         // raw events
3485         /**
3486          * @event beforetoggle
3487          * Fire before toggle the menu
3488          * @param {Roo.EventObject} e
3489          */
3490         "beforetoggle" : true
3491     });
3492 };
3493
3494 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3495     
3496     
3497    
3498     // private
3499     navItems : false,
3500     loadMask : false,
3501     
3502     
3503     getAutoCreate : function(){
3504         
3505         
3506         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3507         
3508     },
3509     
3510     initEvents :function ()
3511     {
3512         //Roo.log(this.el.select('.navbar-toggle',true));
3513         this.el.select('.navbar-toggle',true).on('click', function() {
3514             if(this.fireEvent('beforetoggle', this) !== false){
3515                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3516             }
3517             
3518         }, this);
3519         
3520         var mark = {
3521             tag: "div",
3522             cls:"x-dlg-mask"
3523         };
3524         
3525         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3526         
3527         var size = this.el.getSize();
3528         this.maskEl.setSize(size.width, size.height);
3529         this.maskEl.enableDisplayMode("block");
3530         this.maskEl.hide();
3531         
3532         if(this.loadMask){
3533             this.maskEl.show();
3534         }
3535     },
3536     
3537     
3538     getChildContainer : function()
3539     {
3540         if (this.el.select('.collapse').getCount()) {
3541             return this.el.select('.collapse',true).first();
3542         }
3543         
3544         return this.el;
3545     },
3546     
3547     mask : function()
3548     {
3549         this.maskEl.show();
3550     },
3551     
3552     unmask : function()
3553     {
3554         this.maskEl.hide();
3555     } 
3556     
3557     
3558     
3559     
3560 });
3561
3562
3563
3564  
3565
3566  /*
3567  * - LGPL
3568  *
3569  * navbar
3570  * 
3571  */
3572
3573 /**
3574  * @class Roo.bootstrap.NavSimplebar
3575  * @extends Roo.bootstrap.Navbar
3576  * Bootstrap Sidebar class
3577  *
3578  * @cfg {Boolean} inverse is inverted color
3579  * 
3580  * @cfg {String} type (nav | pills | tabs)
3581  * @cfg {Boolean} arrangement stacked | justified
3582  * @cfg {String} align (left | right) alignment
3583  * 
3584  * @cfg {Boolean} main (true|false) main nav bar? default false
3585  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3586  * 
3587  * @cfg {String} tag (header|footer|nav|div) default is nav 
3588
3589  * 
3590  * 
3591  * 
3592  * @constructor
3593  * Create a new Sidebar
3594  * @param {Object} config The config object
3595  */
3596
3597
3598 Roo.bootstrap.NavSimplebar = function(config){
3599     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3600 };
3601
3602 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3603     
3604     inverse: false,
3605     
3606     type: false,
3607     arrangement: '',
3608     align : false,
3609     
3610     
3611     
3612     main : false,
3613     
3614     
3615     tag : false,
3616     
3617     
3618     getAutoCreate : function(){
3619         
3620         
3621         var cfg = {
3622             tag : this.tag || 'div',
3623             cls : 'navbar'
3624         };
3625           
3626         
3627         cfg.cn = [
3628             {
3629                 cls: 'nav',
3630                 tag : 'ul'
3631             }
3632         ];
3633         
3634          
3635         this.type = this.type || 'nav';
3636         if (['tabs','pills'].indexOf(this.type)!==-1) {
3637             cfg.cn[0].cls += ' nav-' + this.type
3638         
3639         
3640         } else {
3641             if (this.type!=='nav') {
3642                 Roo.log('nav type must be nav/tabs/pills')
3643             }
3644             cfg.cn[0].cls += ' navbar-nav'
3645         }
3646         
3647         
3648         
3649         
3650         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3651             cfg.cn[0].cls += ' nav-' + this.arrangement;
3652         }
3653         
3654         
3655         if (this.align === 'right') {
3656             cfg.cn[0].cls += ' navbar-right';
3657         }
3658         
3659         if (this.inverse) {
3660             cfg.cls += ' navbar-inverse';
3661             
3662         }
3663         
3664         
3665         return cfg;
3666     
3667         
3668     }
3669     
3670     
3671     
3672 });
3673
3674
3675
3676  
3677
3678  
3679        /*
3680  * - LGPL
3681  *
3682  * navbar
3683  * 
3684  */
3685
3686 /**
3687  * @class Roo.bootstrap.NavHeaderbar
3688  * @extends Roo.bootstrap.NavSimplebar
3689  * Bootstrap Sidebar class
3690  *
3691  * @cfg {String} brand what is brand
3692  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3693  * @cfg {String} brand_href href of the brand
3694  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3695  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3696  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3697  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3698  * 
3699  * @constructor
3700  * Create a new Sidebar
3701  * @param {Object} config The config object
3702  */
3703
3704
3705 Roo.bootstrap.NavHeaderbar = function(config){
3706     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3707       
3708 };
3709
3710 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3711     
3712     position: '',
3713     brand: '',
3714     brand_href: false,
3715     srButton : true,
3716     autohide : false,
3717     desktopCenter : false,
3718    
3719     
3720     getAutoCreate : function(){
3721         
3722         var   cfg = {
3723             tag: this.nav || 'nav',
3724             cls: 'navbar',
3725             role: 'navigation',
3726             cn: []
3727         };
3728         
3729         var cn = cfg.cn;
3730         if (this.desktopCenter) {
3731             cn.push({cls : 'container', cn : []});
3732             cn = cn[0].cn;
3733         }
3734         
3735         if(this.srButton){
3736             cn.push({
3737                 tag: 'div',
3738                 cls: 'navbar-header',
3739                 cn: [
3740                     {
3741                         tag: 'button',
3742                         type: 'button',
3743                         cls: 'navbar-toggle',
3744                         'data-toggle': 'collapse',
3745                         cn: [
3746                             {
3747                                 tag: 'span',
3748                                 cls: 'sr-only',
3749                                 html: 'Toggle navigation'
3750                             },
3751                             {
3752                                 tag: 'span',
3753                                 cls: 'icon-bar'
3754                             },
3755                             {
3756                                 tag: 'span',
3757                                 cls: 'icon-bar'
3758                             },
3759                             {
3760                                 tag: 'span',
3761                                 cls: 'icon-bar'
3762                             }
3763                         ]
3764                     }
3765                 ]
3766             });
3767         }
3768         
3769         cn.push({
3770             tag: 'div',
3771             cls: 'collapse navbar-collapse',
3772             cn : []
3773         });
3774         
3775         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3776         
3777         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3778             cfg.cls += ' navbar-' + this.position;
3779             
3780             // tag can override this..
3781             
3782             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3783         }
3784         
3785         if (this.brand !== '') {
3786             cn[0].cn.push({
3787                 tag: 'a',
3788                 href: this.brand_href ? this.brand_href : '#',
3789                 cls: 'navbar-brand',
3790                 cn: [
3791                 this.brand
3792                 ]
3793             });
3794         }
3795         
3796         if(this.main){
3797             cfg.cls += ' main-nav';
3798         }
3799         
3800         
3801         return cfg;
3802
3803         
3804     },
3805     getHeaderChildContainer : function()
3806     {
3807         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3808             return this.el.select('.navbar-header',true).first();
3809         }
3810         
3811         return this.getChildContainer();
3812     },
3813     
3814     
3815     initEvents : function()
3816     {
3817         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3818         
3819         if (this.autohide) {
3820             
3821             var prevScroll = 0;
3822             var ft = this.el;
3823             
3824             Roo.get(document).on('scroll',function(e) {
3825                 var ns = Roo.get(document).getScroll().top;
3826                 var os = prevScroll;
3827                 prevScroll = ns;
3828                 
3829                 if(ns > os){
3830                     ft.removeClass('slideDown');
3831                     ft.addClass('slideUp');
3832                     return;
3833                 }
3834                 ft.removeClass('slideUp');
3835                 ft.addClass('slideDown');
3836                  
3837               
3838           },this);
3839         }
3840     }    
3841     
3842 });
3843
3844
3845
3846  
3847
3848  /*
3849  * - LGPL
3850  *
3851  * navbar
3852  * 
3853  */
3854
3855 /**
3856  * @class Roo.bootstrap.NavSidebar
3857  * @extends Roo.bootstrap.Navbar
3858  * Bootstrap Sidebar class
3859  * 
3860  * @constructor
3861  * Create a new Sidebar
3862  * @param {Object} config The config object
3863  */
3864
3865
3866 Roo.bootstrap.NavSidebar = function(config){
3867     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3868 };
3869
3870 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3871     
3872     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3873     
3874     getAutoCreate : function(){
3875         
3876         
3877         return  {
3878             tag: 'div',
3879             cls: 'sidebar sidebar-nav'
3880         };
3881     
3882         
3883     }
3884     
3885     
3886     
3887 });
3888
3889
3890
3891  
3892
3893  /*
3894  * - LGPL
3895  *
3896  * nav group
3897  * 
3898  */
3899
3900 /**
3901  * @class Roo.bootstrap.NavGroup
3902  * @extends Roo.bootstrap.Component
3903  * Bootstrap NavGroup class
3904  * @cfg {String} align (left|right)
3905  * @cfg {Boolean} inverse
3906  * @cfg {String} type (nav|pills|tab) default nav
3907  * @cfg {String} navId - reference Id for navbar.
3908
3909  * 
3910  * @constructor
3911  * Create a new nav group
3912  * @param {Object} config The config object
3913  */
3914
3915 Roo.bootstrap.NavGroup = function(config){
3916     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3917     this.navItems = [];
3918    
3919     Roo.bootstrap.NavGroup.register(this);
3920      this.addEvents({
3921         /**
3922              * @event changed
3923              * Fires when the active item changes
3924              * @param {Roo.bootstrap.NavGroup} this
3925              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3926              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3927          */
3928         'changed': true
3929      });
3930     
3931 };
3932
3933 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3934     
3935     align: '',
3936     inverse: false,
3937     form: false,
3938     type: 'nav',
3939     navId : '',
3940     // private
3941     
3942     navItems : false, 
3943     
3944     getAutoCreate : function()
3945     {
3946         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3947         
3948         cfg = {
3949             tag : 'ul',
3950             cls: 'nav' 
3951         };
3952         
3953         if (['tabs','pills'].indexOf(this.type)!==-1) {
3954             cfg.cls += ' nav-' + this.type
3955         } else {
3956             if (this.type!=='nav') {
3957                 Roo.log('nav type must be nav/tabs/pills')
3958             }
3959             cfg.cls += ' navbar-nav'
3960         }
3961         
3962         if (this.parent().sidebar) {
3963             cfg = {
3964                 tag: 'ul',
3965                 cls: 'dashboard-menu sidebar-menu'
3966             };
3967             
3968             return cfg;
3969         }
3970         
3971         if (this.form === true) {
3972             cfg = {
3973                 tag: 'form',
3974                 cls: 'navbar-form'
3975             };
3976             
3977             if (this.align === 'right') {
3978                 cfg.cls += ' navbar-right';
3979             } else {
3980                 cfg.cls += ' navbar-left';
3981             }
3982         }
3983         
3984         if (this.align === 'right') {
3985             cfg.cls += ' navbar-right';
3986         }
3987         
3988         if (this.inverse) {
3989             cfg.cls += ' navbar-inverse';
3990             
3991         }
3992         
3993         
3994         return cfg;
3995     },
3996     /**
3997     * sets the active Navigation item
3998     * @param {Roo.bootstrap.NavItem} the new current navitem
3999     */
4000     setActiveItem : function(item)
4001     {
4002         var prev = false;
4003         Roo.each(this.navItems, function(v){
4004             if (v == item) {
4005                 return ;
4006             }
4007             if (v.isActive()) {
4008                 v.setActive(false, true);
4009                 prev = v;
4010                 
4011             }
4012             
4013         });
4014
4015         item.setActive(true, true);
4016         this.fireEvent('changed', this, item, prev);
4017         
4018         
4019     },
4020     /**
4021     * gets the active Navigation item
4022     * @return {Roo.bootstrap.NavItem} the current navitem
4023     */
4024     getActive : function()
4025     {
4026         
4027         var prev = false;
4028         Roo.each(this.navItems, function(v){
4029             
4030             if (v.isActive()) {
4031                 prev = v;
4032                 
4033             }
4034             
4035         });
4036         return prev;
4037     },
4038     
4039     indexOfNav : function()
4040     {
4041         
4042         var prev = false;
4043         Roo.each(this.navItems, function(v,i){
4044             
4045             if (v.isActive()) {
4046                 prev = i;
4047                 
4048             }
4049             
4050         });
4051         return prev;
4052     },
4053     /**
4054     * adds a Navigation item
4055     * @param {Roo.bootstrap.NavItem} the navitem to add
4056     */
4057     addItem : function(cfg)
4058     {
4059         var cn = new Roo.bootstrap.NavItem(cfg);
4060         this.register(cn);
4061         cn.parentId = this.id;
4062         cn.onRender(this.el, null);
4063         return cn;
4064     },
4065     /**
4066     * register a Navigation item
4067     * @param {Roo.bootstrap.NavItem} the navitem to add
4068     */
4069     register : function(item)
4070     {
4071         this.navItems.push( item);
4072         item.navId = this.navId;
4073     
4074     },
4075     
4076     /**
4077     * clear all the Navigation item
4078     */
4079    
4080     clearAll : function()
4081     {
4082         this.navItems = [];
4083         this.el.dom.innerHTML = '';
4084     },
4085     
4086     getNavItem: function(tabId)
4087     {
4088         var ret = false;
4089         Roo.each(this.navItems, function(e) {
4090             if (e.tabId == tabId) {
4091                ret =  e;
4092                return false;
4093             }
4094             return true;
4095             
4096         });
4097         return ret;
4098     },
4099     
4100     setActiveNext : function()
4101     {
4102         var i = this.indexOfNav(this.getActive());
4103         if (i > this.navItems.length) {
4104             return;
4105         }
4106         this.setActiveItem(this.navItems[i+1]);
4107     },
4108     setActivePrev : function()
4109     {
4110         var i = this.indexOfNav(this.getActive());
4111         if (i  < 1) {
4112             return;
4113         }
4114         this.setActiveItem(this.navItems[i-1]);
4115     },
4116     clearWasActive : function(except) {
4117         Roo.each(this.navItems, function(e) {
4118             if (e.tabId != except.tabId && e.was_active) {
4119                e.was_active = false;
4120                return false;
4121             }
4122             return true;
4123             
4124         });
4125     },
4126     getWasActive : function ()
4127     {
4128         var r = false;
4129         Roo.each(this.navItems, function(e) {
4130             if (e.was_active) {
4131                r = e;
4132                return false;
4133             }
4134             return true;
4135             
4136         });
4137         return r;
4138     }
4139     
4140     
4141 });
4142
4143  
4144 Roo.apply(Roo.bootstrap.NavGroup, {
4145     
4146     groups: {},
4147      /**
4148     * register a Navigation Group
4149     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4150     */
4151     register : function(navgrp)
4152     {
4153         this.groups[navgrp.navId] = navgrp;
4154         
4155     },
4156     /**
4157     * fetch a Navigation Group based on the navigation ID
4158     * @param {string} the navgroup to add
4159     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4160     */
4161     get: function(navId) {
4162         if (typeof(this.groups[navId]) == 'undefined') {
4163             return false;
4164             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4165         }
4166         return this.groups[navId] ;
4167     }
4168     
4169     
4170     
4171 });
4172
4173  /*
4174  * - LGPL
4175  *
4176  * row
4177  * 
4178  */
4179
4180 /**
4181  * @class Roo.bootstrap.NavItem
4182  * @extends Roo.bootstrap.Component
4183  * Bootstrap Navbar.NavItem class
4184  * @cfg {String} href  link to
4185  * @cfg {String} html content of button
4186  * @cfg {String} badge text inside badge
4187  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4188  * @cfg {String} glyphicon name of glyphicon
4189  * @cfg {String} icon name of font awesome icon
4190  * @cfg {Boolean} active Is item active
4191  * @cfg {Boolean} disabled Is item disabled
4192  
4193  * @cfg {Boolean} preventDefault (true | false) default false
4194  * @cfg {String} tabId the tab that this item activates.
4195  * @cfg {String} tagtype (a|span) render as a href or span?
4196  * @cfg {Boolean} animateRef (true|false) link to element default false  
4197   
4198  * @constructor
4199  * Create a new Navbar Item
4200  * @param {Object} config The config object
4201  */
4202 Roo.bootstrap.NavItem = function(config){
4203     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4204     this.addEvents({
4205         // raw events
4206         /**
4207          * @event click
4208          * The raw click event for the entire grid.
4209          * @param {Roo.EventObject} e
4210          */
4211         "click" : true,
4212          /**
4213             * @event changed
4214             * Fires when the active item active state changes
4215             * @param {Roo.bootstrap.NavItem} this
4216             * @param {boolean} state the new state
4217              
4218          */
4219         'changed': true,
4220         /**
4221             * @event scrollto
4222             * Fires when scroll to element
4223             * @param {Roo.bootstrap.NavItem} this
4224             * @param {Object} options
4225             * @param {Roo.EventObject} e
4226              
4227          */
4228         'scrollto': true
4229     });
4230    
4231 };
4232
4233 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4234     
4235     href: false,
4236     html: '',
4237     badge: '',
4238     icon: false,
4239     glyphicon: false,
4240     active: false,
4241     preventDefault : false,
4242     tabId : false,
4243     tagtype : 'a',
4244     disabled : false,
4245     animateRef : false,
4246     was_active : false,
4247     
4248     getAutoCreate : function(){
4249          
4250         var cfg = {
4251             tag: 'li',
4252             cls: 'nav-item'
4253             
4254         };
4255         
4256         if (this.active) {
4257             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4258         }
4259         if (this.disabled) {
4260             cfg.cls += ' disabled';
4261         }
4262         
4263         if (this.href || this.html || this.glyphicon || this.icon) {
4264             cfg.cn = [
4265                 {
4266                     tag: this.tagtype,
4267                     href : this.href || "#",
4268                     html: this.html || ''
4269                 }
4270             ];
4271             
4272             if (this.icon) {
4273                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4274             }
4275
4276             if(this.glyphicon) {
4277                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4278             }
4279             
4280             if (this.menu) {
4281                 
4282                 cfg.cn[0].html += " <span class='caret'></span>";
4283              
4284             }
4285             
4286             if (this.badge !== '') {
4287                  
4288                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4289             }
4290         }
4291         
4292         
4293         
4294         return cfg;
4295     },
4296     initEvents: function() 
4297     {
4298         if (typeof (this.menu) != 'undefined') {
4299             this.menu.parentType = this.xtype;
4300             this.menu.triggerEl = this.el;
4301             this.menu = this.addxtype(Roo.apply({}, this.menu));
4302         }
4303         
4304         this.el.select('a',true).on('click', this.onClick, this);
4305         
4306         if(this.tagtype == 'span'){
4307             this.el.select('span',true).on('click', this.onClick, this);
4308         }
4309        
4310         // at this point parent should be available..
4311         this.parent().register(this);
4312     },
4313     
4314     onClick : function(e)
4315     {
4316         if (e.getTarget('.dropdown-menu-item')) {
4317             // did you click on a menu itemm.... - then don't trigger onclick..
4318             return;
4319         }
4320         
4321         if(
4322                 this.preventDefault || 
4323                 this.href == '#' 
4324         ){
4325             Roo.log("NavItem - prevent Default?");
4326             e.preventDefault();
4327         }
4328         
4329         if (this.disabled) {
4330             return;
4331         }
4332         
4333         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4334         if (tg && tg.transition) {
4335             Roo.log("waiting for the transitionend");
4336             return;
4337         }
4338         
4339         
4340         
4341         //Roo.log("fire event clicked");
4342         if(this.fireEvent('click', this, e) === false){
4343             return;
4344         };
4345         
4346         if(this.tagtype == 'span'){
4347             return;
4348         }
4349         
4350         //Roo.log(this.href);
4351         var ael = this.el.select('a',true).first();
4352         //Roo.log(ael);
4353         
4354         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4355             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4356             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4357                 return; // ignore... - it's a 'hash' to another page.
4358             }
4359             Roo.log("NavItem - prevent Default?");
4360             e.preventDefault();
4361             this.scrollToElement(e);
4362         }
4363         
4364         
4365         var p =  this.parent();
4366    
4367         if (['tabs','pills'].indexOf(p.type)!==-1) {
4368             if (typeof(p.setActiveItem) !== 'undefined') {
4369                 p.setActiveItem(this);
4370             }
4371         }
4372         
4373         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4374         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4375             // remove the collapsed menu expand...
4376             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4377         }
4378     },
4379     
4380     isActive: function () {
4381         return this.active
4382     },
4383     setActive : function(state, fire, is_was_active)
4384     {
4385         if (this.active && !state && this.navId) {
4386             this.was_active = true;
4387             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4388             if (nv) {
4389                 nv.clearWasActive(this);
4390             }
4391             
4392         }
4393         this.active = state;
4394         
4395         if (!state ) {
4396             this.el.removeClass('active');
4397         } else if (!this.el.hasClass('active')) {
4398             this.el.addClass('active');
4399         }
4400         if (fire) {
4401             this.fireEvent('changed', this, state);
4402         }
4403         
4404         // show a panel if it's registered and related..
4405         
4406         if (!this.navId || !this.tabId || !state || is_was_active) {
4407             return;
4408         }
4409         
4410         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4411         if (!tg) {
4412             return;
4413         }
4414         var pan = tg.getPanelByName(this.tabId);
4415         if (!pan) {
4416             return;
4417         }
4418         // if we can not flip to new panel - go back to old nav highlight..
4419         if (false == tg.showPanel(pan)) {
4420             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4421             if (nv) {
4422                 var onav = nv.getWasActive();
4423                 if (onav) {
4424                     onav.setActive(true, false, true);
4425                 }
4426             }
4427             
4428         }
4429         
4430         
4431         
4432     },
4433      // this should not be here...
4434     setDisabled : function(state)
4435     {
4436         this.disabled = state;
4437         if (!state ) {
4438             this.el.removeClass('disabled');
4439         } else if (!this.el.hasClass('disabled')) {
4440             this.el.addClass('disabled');
4441         }
4442         
4443     },
4444     
4445     /**
4446      * Fetch the element to display the tooltip on.
4447      * @return {Roo.Element} defaults to this.el
4448      */
4449     tooltipEl : function()
4450     {
4451         return this.el.select('' + this.tagtype + '', true).first();
4452     },
4453     
4454     scrollToElement : function(e)
4455     {
4456         var c = document.body;
4457         
4458         /*
4459          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4460          */
4461         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4462             c = document.documentElement;
4463         }
4464         
4465         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4466         
4467         if(!target){
4468             return;
4469         }
4470
4471         var o = target.calcOffsetsTo(c);
4472         
4473         var options = {
4474             target : target,
4475             value : o[1]
4476         };
4477         
4478         this.fireEvent('scrollto', this, options, e);
4479         
4480         Roo.get(c).scrollTo('top', options.value, true);
4481         
4482         return;
4483     }
4484 });
4485  
4486
4487  /*
4488  * - LGPL
4489  *
4490  * sidebar item
4491  *
4492  *  li
4493  *    <span> icon </span>
4494  *    <span> text </span>
4495  *    <span>badge </span>
4496  */
4497
4498 /**
4499  * @class Roo.bootstrap.NavSidebarItem
4500  * @extends Roo.bootstrap.NavItem
4501  * Bootstrap Navbar.NavSidebarItem class
4502  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4503  * {bool} open is the menu open
4504  * @constructor
4505  * Create a new Navbar Button
4506  * @param {Object} config The config object
4507  */
4508 Roo.bootstrap.NavSidebarItem = function(config){
4509     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4510     this.addEvents({
4511         // raw events
4512         /**
4513          * @event click
4514          * The raw click event for the entire grid.
4515          * @param {Roo.EventObject} e
4516          */
4517         "click" : true,
4518          /**
4519             * @event changed
4520             * Fires when the active item active state changes
4521             * @param {Roo.bootstrap.NavSidebarItem} this
4522             * @param {boolean} state the new state
4523              
4524          */
4525         'changed': true
4526     });
4527    
4528 };
4529
4530 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4531     
4532     badgeWeight : 'default',
4533     
4534     open: false,
4535     
4536     getAutoCreate : function(){
4537         
4538         
4539         var a = {
4540                 tag: 'a',
4541                 href : this.href || '#',
4542                 cls: '',
4543                 html : '',
4544                 cn : []
4545         };
4546         var cfg = {
4547             tag: 'li',
4548             cls: '',
4549             cn: [ a ]
4550         };
4551         var span = {
4552             tag: 'span',
4553             html : this.html || ''
4554         };
4555         
4556         
4557         if (this.active) {
4558             cfg.cls += ' active';
4559         }
4560         
4561         if (this.disabled) {
4562             cfg.cls += ' disabled';
4563         }
4564         if (this.open) {
4565             cfg.cls += ' open x-open';
4566         }
4567         // left icon..
4568         if (this.glyphicon || this.icon) {
4569             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4570             a.cn.push({ tag : 'i', cls : c }) ;
4571         }
4572         // html..
4573         a.cn.push(span);
4574         // then badge..
4575         if (this.badge !== '') {
4576             
4577             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4578         }
4579         // fi
4580         if (this.menu) {
4581             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4582             a.cls += 'dropdown-toggle treeview' ;
4583         }
4584         
4585         return cfg;
4586          
4587            
4588     },
4589     
4590     initEvents : function()
4591     { 
4592         if (typeof (this.menu) != 'undefined') {
4593             this.menu.parentType = this.xtype;
4594             this.menu.triggerEl = this.el;
4595             this.menu = this.addxtype(Roo.apply({}, this.menu));
4596         }
4597         
4598         this.el.on('click', this.onClick, this);
4599        
4600     
4601         if(this.badge !== ''){
4602  
4603             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4604         }
4605         
4606     },
4607     
4608     onClick : function(e)
4609     {
4610         if(this.disabled){
4611             e.preventDefault();
4612             return;
4613         }
4614         
4615         if(this.preventDefault){
4616             e.preventDefault();
4617         }
4618         
4619         this.fireEvent('click', this);
4620     },
4621     
4622     disable : function()
4623     {
4624         this.setDisabled(true);
4625     },
4626     
4627     enable : function()
4628     {
4629         this.setDisabled(false);
4630     },
4631     
4632     setDisabled : function(state)
4633     {
4634         if(this.disabled == state){
4635             return;
4636         }
4637         
4638         this.disabled = state;
4639         
4640         if (state) {
4641             this.el.addClass('disabled');
4642             return;
4643         }
4644         
4645         this.el.removeClass('disabled');
4646         
4647         return;
4648     },
4649     
4650     setActive : function(state)
4651     {
4652         if(this.active == state){
4653             return;
4654         }
4655         
4656         this.active = state;
4657         
4658         if (state) {
4659             this.el.addClass('active');
4660             return;
4661         }
4662         
4663         this.el.removeClass('active');
4664         
4665         return;
4666     },
4667     
4668     isActive: function () 
4669     {
4670         return this.active;
4671     },
4672     
4673     setBadge : function(str)
4674     {
4675         if(!this.badgeEl){
4676             return;
4677         }
4678         
4679         this.badgeEl.dom.innerHTML = str;
4680     }
4681     
4682    
4683      
4684  
4685 });
4686  
4687
4688  /*
4689  * - LGPL
4690  *
4691  * row
4692  * 
4693  */
4694
4695 /**
4696  * @class Roo.bootstrap.Row
4697  * @extends Roo.bootstrap.Component
4698  * Bootstrap Row class (contains columns...)
4699  * 
4700  * @constructor
4701  * Create a new Row
4702  * @param {Object} config The config object
4703  */
4704
4705 Roo.bootstrap.Row = function(config){
4706     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4707 };
4708
4709 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4710     
4711     getAutoCreate : function(){
4712        return {
4713             cls: 'row clearfix'
4714        };
4715     }
4716     
4717     
4718 });
4719
4720  
4721
4722  /*
4723  * - LGPL
4724  *
4725  * element
4726  * 
4727  */
4728
4729 /**
4730  * @class Roo.bootstrap.Element
4731  * @extends Roo.bootstrap.Component
4732  * Bootstrap Element class
4733  * @cfg {String} html contents of the element
4734  * @cfg {String} tag tag of the element
4735  * @cfg {String} cls class of the element
4736  * @cfg {Boolean} preventDefault (true|false) default false
4737  * @cfg {Boolean} clickable (true|false) default false
4738  * 
4739  * @constructor
4740  * Create a new Element
4741  * @param {Object} config The config object
4742  */
4743
4744 Roo.bootstrap.Element = function(config){
4745     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4746     
4747     this.addEvents({
4748         // raw events
4749         /**
4750          * @event click
4751          * When a element is chick
4752          * @param {Roo.bootstrap.Element} this
4753          * @param {Roo.EventObject} e
4754          */
4755         "click" : true
4756     });
4757 };
4758
4759 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4760     
4761     tag: 'div',
4762     cls: '',
4763     html: '',
4764     preventDefault: false, 
4765     clickable: false,
4766     
4767     getAutoCreate : function(){
4768         
4769         var cfg = {
4770             tag: this.tag,
4771             cls: this.cls,
4772             html: this.html
4773         };
4774         
4775         return cfg;
4776     },
4777     
4778     initEvents: function() 
4779     {
4780         Roo.bootstrap.Element.superclass.initEvents.call(this);
4781         
4782         if(this.clickable){
4783             this.el.on('click', this.onClick, this);
4784         }
4785         
4786     },
4787     
4788     onClick : function(e)
4789     {
4790         if(this.preventDefault){
4791             e.preventDefault();
4792         }
4793         
4794         this.fireEvent('click', this, e);
4795     },
4796     
4797     getValue : function()
4798     {
4799         return this.el.dom.innerHTML;
4800     },
4801     
4802     setValue : function(value)
4803     {
4804         this.el.dom.innerHTML = value;
4805     }
4806    
4807 });
4808
4809  
4810
4811  /*
4812  * - LGPL
4813  *
4814  * pagination
4815  * 
4816  */
4817
4818 /**
4819  * @class Roo.bootstrap.Pagination
4820  * @extends Roo.bootstrap.Component
4821  * Bootstrap Pagination class
4822  * @cfg {String} size xs | sm | md | lg
4823  * @cfg {Boolean} inverse false | true
4824  * 
4825  * @constructor
4826  * Create a new Pagination
4827  * @param {Object} config The config object
4828  */
4829
4830 Roo.bootstrap.Pagination = function(config){
4831     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4832 };
4833
4834 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4835     
4836     cls: false,
4837     size: false,
4838     inverse: false,
4839     
4840     getAutoCreate : function(){
4841         var cfg = {
4842             tag: 'ul',
4843                 cls: 'pagination'
4844         };
4845         if (this.inverse) {
4846             cfg.cls += ' inverse';
4847         }
4848         if (this.html) {
4849             cfg.html=this.html;
4850         }
4851         if (this.cls) {
4852             cfg.cls += " " + this.cls;
4853         }
4854         return cfg;
4855     }
4856    
4857 });
4858
4859  
4860
4861  /*
4862  * - LGPL
4863  *
4864  * Pagination item
4865  * 
4866  */
4867
4868
4869 /**
4870  * @class Roo.bootstrap.PaginationItem
4871  * @extends Roo.bootstrap.Component
4872  * Bootstrap PaginationItem class
4873  * @cfg {String} html text
4874  * @cfg {String} href the link
4875  * @cfg {Boolean} preventDefault (true | false) default true
4876  * @cfg {Boolean} active (true | false) default false
4877  * @cfg {Boolean} disabled default false
4878  * 
4879  * 
4880  * @constructor
4881  * Create a new PaginationItem
4882  * @param {Object} config The config object
4883  */
4884
4885
4886 Roo.bootstrap.PaginationItem = function(config){
4887     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4888     this.addEvents({
4889         // raw events
4890         /**
4891          * @event click
4892          * The raw click event for the entire grid.
4893          * @param {Roo.EventObject} e
4894          */
4895         "click" : true
4896     });
4897 };
4898
4899 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4900     
4901     href : false,
4902     html : false,
4903     preventDefault: true,
4904     active : false,
4905     cls : false,
4906     disabled: false,
4907     
4908     getAutoCreate : function(){
4909         var cfg= {
4910             tag: 'li',
4911             cn: [
4912                 {
4913                     tag : 'a',
4914                     href : this.href ? this.href : '#',
4915                     html : this.html ? this.html : ''
4916                 }
4917             ]
4918         };
4919         
4920         if(this.cls){
4921             cfg.cls = this.cls;
4922         }
4923         
4924         if(this.disabled){
4925             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4926         }
4927         
4928         if(this.active){
4929             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4930         }
4931         
4932         return cfg;
4933     },
4934     
4935     initEvents: function() {
4936         
4937         this.el.on('click', this.onClick, this);
4938         
4939     },
4940     onClick : function(e)
4941     {
4942         Roo.log('PaginationItem on click ');
4943         if(this.preventDefault){
4944             e.preventDefault();
4945         }
4946         
4947         if(this.disabled){
4948             return;
4949         }
4950         
4951         this.fireEvent('click', this, e);
4952     }
4953    
4954 });
4955
4956  
4957
4958  /*
4959  * - LGPL
4960  *
4961  * slider
4962  * 
4963  */
4964
4965
4966 /**
4967  * @class Roo.bootstrap.Slider
4968  * @extends Roo.bootstrap.Component
4969  * Bootstrap Slider class
4970  *    
4971  * @constructor
4972  * Create a new Slider
4973  * @param {Object} config The config object
4974  */
4975
4976 Roo.bootstrap.Slider = function(config){
4977     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4978 };
4979
4980 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4981     
4982     getAutoCreate : function(){
4983         
4984         var cfg = {
4985             tag: 'div',
4986             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4987             cn: [
4988                 {
4989                     tag: 'a',
4990                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4991                 }
4992             ]
4993         };
4994         
4995         return cfg;
4996     }
4997    
4998 });
4999
5000  /*
5001  * Based on:
5002  * Ext JS Library 1.1.1
5003  * Copyright(c) 2006-2007, Ext JS, LLC.
5004  *
5005  * Originally Released Under LGPL - original licence link has changed is not relivant.
5006  *
5007  * Fork - LGPL
5008  * <script type="text/javascript">
5009  */
5010  
5011
5012 /**
5013  * @class Roo.grid.ColumnModel
5014  * @extends Roo.util.Observable
5015  * This is the default implementation of a ColumnModel used by the Grid. It defines
5016  * the columns in the grid.
5017  * <br>Usage:<br>
5018  <pre><code>
5019  var colModel = new Roo.grid.ColumnModel([
5020         {header: "Ticker", width: 60, sortable: true, locked: true},
5021         {header: "Company Name", width: 150, sortable: true},
5022         {header: "Market Cap.", width: 100, sortable: true},
5023         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5024         {header: "Employees", width: 100, sortable: true, resizable: false}
5025  ]);
5026  </code></pre>
5027  * <p>
5028  
5029  * The config options listed for this class are options which may appear in each
5030  * individual column definition.
5031  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5032  * @constructor
5033  * @param {Object} config An Array of column config objects. See this class's
5034  * config objects for details.
5035 */
5036 Roo.grid.ColumnModel = function(config){
5037         /**
5038      * The config passed into the constructor
5039      */
5040     this.config = config;
5041     this.lookup = {};
5042
5043     // if no id, create one
5044     // if the column does not have a dataIndex mapping,
5045     // map it to the order it is in the config
5046     for(var i = 0, len = config.length; i < len; i++){
5047         var c = config[i];
5048         if(typeof c.dataIndex == "undefined"){
5049             c.dataIndex = i;
5050         }
5051         if(typeof c.renderer == "string"){
5052             c.renderer = Roo.util.Format[c.renderer];
5053         }
5054         if(typeof c.id == "undefined"){
5055             c.id = Roo.id();
5056         }
5057         if(c.editor && c.editor.xtype){
5058             c.editor  = Roo.factory(c.editor, Roo.grid);
5059         }
5060         if(c.editor && c.editor.isFormField){
5061             c.editor = new Roo.grid.GridEditor(c.editor);
5062         }
5063         this.lookup[c.id] = c;
5064     }
5065
5066     /**
5067      * The width of columns which have no width specified (defaults to 100)
5068      * @type Number
5069      */
5070     this.defaultWidth = 100;
5071
5072     /**
5073      * Default sortable of columns which have no sortable specified (defaults to false)
5074      * @type Boolean
5075      */
5076     this.defaultSortable = false;
5077
5078     this.addEvents({
5079         /**
5080              * @event widthchange
5081              * Fires when the width of a column changes.
5082              * @param {ColumnModel} this
5083              * @param {Number} columnIndex The column index
5084              * @param {Number} newWidth The new width
5085              */
5086             "widthchange": true,
5087         /**
5088              * @event headerchange
5089              * Fires when the text of a header changes.
5090              * @param {ColumnModel} this
5091              * @param {Number} columnIndex The column index
5092              * @param {Number} newText The new header text
5093              */
5094             "headerchange": true,
5095         /**
5096              * @event hiddenchange
5097              * Fires when a column is hidden or "unhidden".
5098              * @param {ColumnModel} this
5099              * @param {Number} columnIndex The column index
5100              * @param {Boolean} hidden true if hidden, false otherwise
5101              */
5102             "hiddenchange": true,
5103             /**
5104          * @event columnmoved
5105          * Fires when a column is moved.
5106          * @param {ColumnModel} this
5107          * @param {Number} oldIndex
5108          * @param {Number} newIndex
5109          */
5110         "columnmoved" : true,
5111         /**
5112          * @event columlockchange
5113          * Fires when a column's locked state is changed
5114          * @param {ColumnModel} this
5115          * @param {Number} colIndex
5116          * @param {Boolean} locked true if locked
5117          */
5118         "columnlockchange" : true
5119     });
5120     Roo.grid.ColumnModel.superclass.constructor.call(this);
5121 };
5122 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5123     /**
5124      * @cfg {String} header The header text to display in the Grid view.
5125      */
5126     /**
5127      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5128      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5129      * specified, the column's index is used as an index into the Record's data Array.
5130      */
5131     /**
5132      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5133      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5134      */
5135     /**
5136      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5137      * Defaults to the value of the {@link #defaultSortable} property.
5138      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5139      */
5140     /**
5141      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5142      */
5143     /**
5144      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5145      */
5146     /**
5147      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5148      */
5149     /**
5150      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5151      */
5152     /**
5153      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5154      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5155      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5156      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5157      */
5158        /**
5159      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5160      */
5161     /**
5162      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5163      */
5164     /**
5165      * @cfg {String} cursor (Optional)
5166      */
5167     /**
5168      * @cfg {String} tooltip (Optional)
5169      */
5170     /**
5171      * @cfg {Number} xs (Optional)
5172      */
5173     /**
5174      * @cfg {Number} sm (Optional)
5175      */
5176     /**
5177      * @cfg {Number} md (Optional)
5178      */
5179     /**
5180      * @cfg {Number} lg (Optional)
5181      */
5182     /**
5183      * Returns the id of the column at the specified index.
5184      * @param {Number} index The column index
5185      * @return {String} the id
5186      */
5187     getColumnId : function(index){
5188         return this.config[index].id;
5189     },
5190
5191     /**
5192      * Returns the column for a specified id.
5193      * @param {String} id The column id
5194      * @return {Object} the column
5195      */
5196     getColumnById : function(id){
5197         return this.lookup[id];
5198     },
5199
5200     
5201     /**
5202      * Returns the column for a specified dataIndex.
5203      * @param {String} dataIndex The column dataIndex
5204      * @return {Object|Boolean} the column or false if not found
5205      */
5206     getColumnByDataIndex: function(dataIndex){
5207         var index = this.findColumnIndex(dataIndex);
5208         return index > -1 ? this.config[index] : false;
5209     },
5210     
5211     /**
5212      * Returns the index for a specified column id.
5213      * @param {String} id The column id
5214      * @return {Number} the index, or -1 if not found
5215      */
5216     getIndexById : function(id){
5217         for(var i = 0, len = this.config.length; i < len; i++){
5218             if(this.config[i].id == id){
5219                 return i;
5220             }
5221         }
5222         return -1;
5223     },
5224     
5225     /**
5226      * Returns the index for a specified column dataIndex.
5227      * @param {String} dataIndex The column dataIndex
5228      * @return {Number} the index, or -1 if not found
5229      */
5230     
5231     findColumnIndex : function(dataIndex){
5232         for(var i = 0, len = this.config.length; i < len; i++){
5233             if(this.config[i].dataIndex == dataIndex){
5234                 return i;
5235             }
5236         }
5237         return -1;
5238     },
5239     
5240     
5241     moveColumn : function(oldIndex, newIndex){
5242         var c = this.config[oldIndex];
5243         this.config.splice(oldIndex, 1);
5244         this.config.splice(newIndex, 0, c);
5245         this.dataMap = null;
5246         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5247     },
5248
5249     isLocked : function(colIndex){
5250         return this.config[colIndex].locked === true;
5251     },
5252
5253     setLocked : function(colIndex, value, suppressEvent){
5254         if(this.isLocked(colIndex) == value){
5255             return;
5256         }
5257         this.config[colIndex].locked = value;
5258         if(!suppressEvent){
5259             this.fireEvent("columnlockchange", this, colIndex, value);
5260         }
5261     },
5262
5263     getTotalLockedWidth : function(){
5264         var totalWidth = 0;
5265         for(var i = 0; i < this.config.length; i++){
5266             if(this.isLocked(i) && !this.isHidden(i)){
5267                 this.totalWidth += this.getColumnWidth(i);
5268             }
5269         }
5270         return totalWidth;
5271     },
5272
5273     getLockedCount : function(){
5274         for(var i = 0, len = this.config.length; i < len; i++){
5275             if(!this.isLocked(i)){
5276                 return i;
5277             }
5278         }
5279         
5280         return this.config.length;
5281     },
5282
5283     /**
5284      * Returns the number of columns.
5285      * @return {Number}
5286      */
5287     getColumnCount : function(visibleOnly){
5288         if(visibleOnly === true){
5289             var c = 0;
5290             for(var i = 0, len = this.config.length; i < len; i++){
5291                 if(!this.isHidden(i)){
5292                     c++;
5293                 }
5294             }
5295             return c;
5296         }
5297         return this.config.length;
5298     },
5299
5300     /**
5301      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5302      * @param {Function} fn
5303      * @param {Object} scope (optional)
5304      * @return {Array} result
5305      */
5306     getColumnsBy : function(fn, scope){
5307         var r = [];
5308         for(var i = 0, len = this.config.length; i < len; i++){
5309             var c = this.config[i];
5310             if(fn.call(scope||this, c, i) === true){
5311                 r[r.length] = c;
5312             }
5313         }
5314         return r;
5315     },
5316
5317     /**
5318      * Returns true if the specified column is sortable.
5319      * @param {Number} col The column index
5320      * @return {Boolean}
5321      */
5322     isSortable : function(col){
5323         if(typeof this.config[col].sortable == "undefined"){
5324             return this.defaultSortable;
5325         }
5326         return this.config[col].sortable;
5327     },
5328
5329     /**
5330      * Returns the rendering (formatting) function defined for the column.
5331      * @param {Number} col The column index.
5332      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5333      */
5334     getRenderer : function(col){
5335         if(!this.config[col].renderer){
5336             return Roo.grid.ColumnModel.defaultRenderer;
5337         }
5338         return this.config[col].renderer;
5339     },
5340
5341     /**
5342      * Sets the rendering (formatting) function for a column.
5343      * @param {Number} col The column index
5344      * @param {Function} fn The function to use to process the cell's raw data
5345      * to return HTML markup for the grid view. The render function is called with
5346      * the following parameters:<ul>
5347      * <li>Data value.</li>
5348      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5349      * <li>css A CSS style string to apply to the table cell.</li>
5350      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5351      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5352      * <li>Row index</li>
5353      * <li>Column index</li>
5354      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5355      */
5356     setRenderer : function(col, fn){
5357         this.config[col].renderer = fn;
5358     },
5359
5360     /**
5361      * Returns the width for the specified column.
5362      * @param {Number} col The column index
5363      * @return {Number}
5364      */
5365     getColumnWidth : function(col){
5366         return this.config[col].width * 1 || this.defaultWidth;
5367     },
5368
5369     /**
5370      * Sets the width for a column.
5371      * @param {Number} col The column index
5372      * @param {Number} width The new width
5373      */
5374     setColumnWidth : function(col, width, suppressEvent){
5375         this.config[col].width = width;
5376         this.totalWidth = null;
5377         if(!suppressEvent){
5378              this.fireEvent("widthchange", this, col, width);
5379         }
5380     },
5381
5382     /**
5383      * Returns the total width of all columns.
5384      * @param {Boolean} includeHidden True to include hidden column widths
5385      * @return {Number}
5386      */
5387     getTotalWidth : function(includeHidden){
5388         if(!this.totalWidth){
5389             this.totalWidth = 0;
5390             for(var i = 0, len = this.config.length; i < len; i++){
5391                 if(includeHidden || !this.isHidden(i)){
5392                     this.totalWidth += this.getColumnWidth(i);
5393                 }
5394             }
5395         }
5396         return this.totalWidth;
5397     },
5398
5399     /**
5400      * Returns the header for the specified column.
5401      * @param {Number} col The column index
5402      * @return {String}
5403      */
5404     getColumnHeader : function(col){
5405         return this.config[col].header;
5406     },
5407
5408     /**
5409      * Sets the header for a column.
5410      * @param {Number} col The column index
5411      * @param {String} header The new header
5412      */
5413     setColumnHeader : function(col, header){
5414         this.config[col].header = header;
5415         this.fireEvent("headerchange", this, col, header);
5416     },
5417
5418     /**
5419      * Returns the tooltip for the specified column.
5420      * @param {Number} col The column index
5421      * @return {String}
5422      */
5423     getColumnTooltip : function(col){
5424             return this.config[col].tooltip;
5425     },
5426     /**
5427      * Sets the tooltip for a column.
5428      * @param {Number} col The column index
5429      * @param {String} tooltip The new tooltip
5430      */
5431     setColumnTooltip : function(col, tooltip){
5432             this.config[col].tooltip = tooltip;
5433     },
5434
5435     /**
5436      * Returns the dataIndex for the specified column.
5437      * @param {Number} col The column index
5438      * @return {Number}
5439      */
5440     getDataIndex : function(col){
5441         return this.config[col].dataIndex;
5442     },
5443
5444     /**
5445      * Sets the dataIndex for a column.
5446      * @param {Number} col The column index
5447      * @param {Number} dataIndex The new dataIndex
5448      */
5449     setDataIndex : function(col, dataIndex){
5450         this.config[col].dataIndex = dataIndex;
5451     },
5452
5453     
5454     
5455     /**
5456      * Returns true if the cell is editable.
5457      * @param {Number} colIndex The column index
5458      * @param {Number} rowIndex The row index - this is nto actually used..?
5459      * @return {Boolean}
5460      */
5461     isCellEditable : function(colIndex, rowIndex){
5462         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5463     },
5464
5465     /**
5466      * Returns the editor defined for the cell/column.
5467      * return false or null to disable editing.
5468      * @param {Number} colIndex The column index
5469      * @param {Number} rowIndex The row index
5470      * @return {Object}
5471      */
5472     getCellEditor : function(colIndex, rowIndex){
5473         return this.config[colIndex].editor;
5474     },
5475
5476     /**
5477      * Sets if a column is editable.
5478      * @param {Number} col The column index
5479      * @param {Boolean} editable True if the column is editable
5480      */
5481     setEditable : function(col, editable){
5482         this.config[col].editable = editable;
5483     },
5484
5485
5486     /**
5487      * Returns true if the column is hidden.
5488      * @param {Number} colIndex The column index
5489      * @return {Boolean}
5490      */
5491     isHidden : function(colIndex){
5492         return this.config[colIndex].hidden;
5493     },
5494
5495
5496     /**
5497      * Returns true if the column width cannot be changed
5498      */
5499     isFixed : function(colIndex){
5500         return this.config[colIndex].fixed;
5501     },
5502
5503     /**
5504      * Returns true if the column can be resized
5505      * @return {Boolean}
5506      */
5507     isResizable : function(colIndex){
5508         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5509     },
5510     /**
5511      * Sets if a column is hidden.
5512      * @param {Number} colIndex The column index
5513      * @param {Boolean} hidden True if the column is hidden
5514      */
5515     setHidden : function(colIndex, hidden){
5516         this.config[colIndex].hidden = hidden;
5517         this.totalWidth = null;
5518         this.fireEvent("hiddenchange", this, colIndex, hidden);
5519     },
5520
5521     /**
5522      * Sets the editor for a column.
5523      * @param {Number} col The column index
5524      * @param {Object} editor The editor object
5525      */
5526     setEditor : function(col, editor){
5527         this.config[col].editor = editor;
5528     }
5529 });
5530
5531 Roo.grid.ColumnModel.defaultRenderer = function(value){
5532         if(typeof value == "string" && value.length < 1){
5533             return "&#160;";
5534         }
5535         return value;
5536 };
5537
5538 // Alias for backwards compatibility
5539 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5540 /*
5541  * Based on:
5542  * Ext JS Library 1.1.1
5543  * Copyright(c) 2006-2007, Ext JS, LLC.
5544  *
5545  * Originally Released Under LGPL - original licence link has changed is not relivant.
5546  *
5547  * Fork - LGPL
5548  * <script type="text/javascript">
5549  */
5550  
5551 /**
5552  * @class Roo.LoadMask
5553  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5554  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5555  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5556  * element's UpdateManager load indicator and will be destroyed after the initial load.
5557  * @constructor
5558  * Create a new LoadMask
5559  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5560  * @param {Object} config The config object
5561  */
5562 Roo.LoadMask = function(el, config){
5563     this.el = Roo.get(el);
5564     Roo.apply(this, config);
5565     if(this.store){
5566         this.store.on('beforeload', this.onBeforeLoad, this);
5567         this.store.on('load', this.onLoad, this);
5568         this.store.on('loadexception', this.onLoadException, this);
5569         this.removeMask = false;
5570     }else{
5571         var um = this.el.getUpdateManager();
5572         um.showLoadIndicator = false; // disable the default indicator
5573         um.on('beforeupdate', this.onBeforeLoad, this);
5574         um.on('update', this.onLoad, this);
5575         um.on('failure', this.onLoad, this);
5576         this.removeMask = true;
5577     }
5578 };
5579
5580 Roo.LoadMask.prototype = {
5581     /**
5582      * @cfg {Boolean} removeMask
5583      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5584      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5585      */
5586     /**
5587      * @cfg {String} msg
5588      * The text to display in a centered loading message box (defaults to 'Loading...')
5589      */
5590     msg : 'Loading...',
5591     /**
5592      * @cfg {String} msgCls
5593      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5594      */
5595     msgCls : 'x-mask-loading',
5596
5597     /**
5598      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5599      * @type Boolean
5600      */
5601     disabled: false,
5602
5603     /**
5604      * Disables the mask to prevent it from being displayed
5605      */
5606     disable : function(){
5607        this.disabled = true;
5608     },
5609
5610     /**
5611      * Enables the mask so that it can be displayed
5612      */
5613     enable : function(){
5614         this.disabled = false;
5615     },
5616     
5617     onLoadException : function()
5618     {
5619         Roo.log(arguments);
5620         
5621         if (typeof(arguments[3]) != 'undefined') {
5622             Roo.MessageBox.alert("Error loading",arguments[3]);
5623         } 
5624         /*
5625         try {
5626             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5627                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5628             }   
5629         } catch(e) {
5630             
5631         }
5632         */
5633     
5634         
5635         
5636         this.el.unmask(this.removeMask);
5637     },
5638     // private
5639     onLoad : function()
5640     {
5641         this.el.unmask(this.removeMask);
5642     },
5643
5644     // private
5645     onBeforeLoad : function(){
5646         if(!this.disabled){
5647             this.el.mask(this.msg, this.msgCls);
5648         }
5649     },
5650
5651     // private
5652     destroy : function(){
5653         if(this.store){
5654             this.store.un('beforeload', this.onBeforeLoad, this);
5655             this.store.un('load', this.onLoad, this);
5656             this.store.un('loadexception', this.onLoadException, this);
5657         }else{
5658             var um = this.el.getUpdateManager();
5659             um.un('beforeupdate', this.onBeforeLoad, this);
5660             um.un('update', this.onLoad, this);
5661             um.un('failure', this.onLoad, this);
5662         }
5663     }
5664 };/*
5665  * - LGPL
5666  *
5667  * table
5668  * 
5669  */
5670
5671 /**
5672  * @class Roo.bootstrap.Table
5673  * @extends Roo.bootstrap.Component
5674  * Bootstrap Table class
5675  * @cfg {String} cls table class
5676  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5677  * @cfg {String} bgcolor Specifies the background color for a table
5678  * @cfg {Number} border Specifies whether the table cells should have borders or not
5679  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5680  * @cfg {Number} cellspacing Specifies the space between cells
5681  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5682  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5683  * @cfg {String} sortable Specifies that the table should be sortable
5684  * @cfg {String} summary Specifies a summary of the content of a table
5685  * @cfg {Number} width Specifies the width of a table
5686  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5687  * 
5688  * @cfg {boolean} striped Should the rows be alternative striped
5689  * @cfg {boolean} bordered Add borders to the table
5690  * @cfg {boolean} hover Add hover highlighting
5691  * @cfg {boolean} condensed Format condensed
5692  * @cfg {boolean} responsive Format condensed
5693  * @cfg {Boolean} loadMask (true|false) default false
5694  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5695  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5696  * @cfg {Boolean} rowSelection (true|false) default false
5697  * @cfg {Boolean} cellSelection (true|false) default false
5698  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5699  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5700  
5701  * 
5702  * @constructor
5703  * Create a new Table
5704  * @param {Object} config The config object
5705  */
5706
5707 Roo.bootstrap.Table = function(config){
5708     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5709     
5710   
5711     
5712     // BC...
5713     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5714     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5715     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5716     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5717     
5718     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5719     if (this.sm) {
5720         this.sm.grid = this;
5721         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5722         this.sm = this.selModel;
5723         this.sm.xmodule = this.xmodule || false;
5724     }
5725     
5726     if (this.cm && typeof(this.cm.config) == 'undefined') {
5727         this.colModel = new Roo.grid.ColumnModel(this.cm);
5728         this.cm = this.colModel;
5729         this.cm.xmodule = this.xmodule || false;
5730     }
5731     if (this.store) {
5732         this.store= Roo.factory(this.store, Roo.data);
5733         this.ds = this.store;
5734         this.ds.xmodule = this.xmodule || false;
5735          
5736     }
5737     if (this.footer && this.store) {
5738         this.footer.dataSource = this.ds;
5739         this.footer = Roo.factory(this.footer);
5740     }
5741     
5742     /** @private */
5743     this.addEvents({
5744         /**
5745          * @event cellclick
5746          * Fires when a cell is clicked
5747          * @param {Roo.bootstrap.Table} this
5748          * @param {Roo.Element} el
5749          * @param {Number} rowIndex
5750          * @param {Number} columnIndex
5751          * @param {Roo.EventObject} e
5752          */
5753         "cellclick" : true,
5754         /**
5755          * @event celldblclick
5756          * Fires when a cell is double clicked
5757          * @param {Roo.bootstrap.Table} this
5758          * @param {Roo.Element} el
5759          * @param {Number} rowIndex
5760          * @param {Number} columnIndex
5761          * @param {Roo.EventObject} e
5762          */
5763         "celldblclick" : true,
5764         /**
5765          * @event rowclick
5766          * Fires when a row is clicked
5767          * @param {Roo.bootstrap.Table} this
5768          * @param {Roo.Element} el
5769          * @param {Number} rowIndex
5770          * @param {Roo.EventObject} e
5771          */
5772         "rowclick" : true,
5773         /**
5774          * @event rowdblclick
5775          * Fires when a row is double clicked
5776          * @param {Roo.bootstrap.Table} this
5777          * @param {Roo.Element} el
5778          * @param {Number} rowIndex
5779          * @param {Roo.EventObject} e
5780          */
5781         "rowdblclick" : true,
5782         /**
5783          * @event mouseover
5784          * Fires when a mouseover occur
5785          * @param {Roo.bootstrap.Table} this
5786          * @param {Roo.Element} el
5787          * @param {Number} rowIndex
5788          * @param {Number} columnIndex
5789          * @param {Roo.EventObject} e
5790          */
5791         "mouseover" : true,
5792         /**
5793          * @event mouseout
5794          * Fires when a mouseout occur
5795          * @param {Roo.bootstrap.Table} this
5796          * @param {Roo.Element} el
5797          * @param {Number} rowIndex
5798          * @param {Number} columnIndex
5799          * @param {Roo.EventObject} e
5800          */
5801         "mouseout" : true,
5802         /**
5803          * @event rowclass
5804          * Fires when a row is rendered, so you can change add a style to it.
5805          * @param {Roo.bootstrap.Table} this
5806          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5807          */
5808         'rowclass' : true,
5809           /**
5810          * @event rowsrendered
5811          * Fires when all the  rows have been rendered
5812          * @param {Roo.bootstrap.Table} this
5813          */
5814         'rowsrendered' : true,
5815         /**
5816          * @event contextmenu
5817          * The raw contextmenu event for the entire grid.
5818          * @param {Roo.EventObject} e
5819          */
5820         "contextmenu" : true,
5821         /**
5822          * @event rowcontextmenu
5823          * Fires when a row is right clicked
5824          * @param {Roo.bootstrap.Table} this
5825          * @param {Number} rowIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "rowcontextmenu" : true,
5829         /**
5830          * @event cellcontextmenu
5831          * Fires when a cell is right clicked
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Number} rowIndex
5834          * @param {Number} cellIndex
5835          * @param {Roo.EventObject} e
5836          */
5837          "cellcontextmenu" : true,
5838          /**
5839          * @event headercontextmenu
5840          * Fires when a header is right clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Number} columnIndex
5843          * @param {Roo.EventObject} e
5844          */
5845         "headercontextmenu" : true
5846     });
5847 };
5848
5849 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5850     
5851     cls: false,
5852     align: false,
5853     bgcolor: false,
5854     border: false,
5855     cellpadding: false,
5856     cellspacing: false,
5857     frame: false,
5858     rules: false,
5859     sortable: false,
5860     summary: false,
5861     width: false,
5862     striped : false,
5863     scrollBody : false,
5864     bordered: false,
5865     hover:  false,
5866     condensed : false,
5867     responsive : false,
5868     sm : false,
5869     cm : false,
5870     store : false,
5871     loadMask : false,
5872     footerShow : true,
5873     headerShow : true,
5874   
5875     rowSelection : false,
5876     cellSelection : false,
5877     layout : false,
5878     
5879     // Roo.Element - the tbody
5880     mainBody: false,
5881     // Roo.Element - thead element
5882     mainHead: false,
5883     
5884     container: false, // used by gridpanel...
5885     
5886     getAutoCreate : function()
5887     {
5888         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5889         
5890         cfg = {
5891             tag: 'table',
5892             cls : 'table',
5893             cn : []
5894         };
5895         if (this.scrollBody) {
5896             cfg.cls += ' table-body-fixed';
5897         }    
5898         if (this.striped) {
5899             cfg.cls += ' table-striped';
5900         }
5901         
5902         if (this.hover) {
5903             cfg.cls += ' table-hover';
5904         }
5905         if (this.bordered) {
5906             cfg.cls += ' table-bordered';
5907         }
5908         if (this.condensed) {
5909             cfg.cls += ' table-condensed';
5910         }
5911         if (this.responsive) {
5912             cfg.cls += ' table-responsive';
5913         }
5914         
5915         if (this.cls) {
5916             cfg.cls+=  ' ' +this.cls;
5917         }
5918         
5919         // this lot should be simplifed...
5920         
5921         if (this.align) {
5922             cfg.align=this.align;
5923         }
5924         if (this.bgcolor) {
5925             cfg.bgcolor=this.bgcolor;
5926         }
5927         if (this.border) {
5928             cfg.border=this.border;
5929         }
5930         if (this.cellpadding) {
5931             cfg.cellpadding=this.cellpadding;
5932         }
5933         if (this.cellspacing) {
5934             cfg.cellspacing=this.cellspacing;
5935         }
5936         if (this.frame) {
5937             cfg.frame=this.frame;
5938         }
5939         if (this.rules) {
5940             cfg.rules=this.rules;
5941         }
5942         if (this.sortable) {
5943             cfg.sortable=this.sortable;
5944         }
5945         if (this.summary) {
5946             cfg.summary=this.summary;
5947         }
5948         if (this.width) {
5949             cfg.width=this.width;
5950         }
5951         if (this.layout) {
5952             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5953         }
5954         
5955         if(this.store || this.cm){
5956             if(this.headerShow){
5957                 cfg.cn.push(this.renderHeader());
5958             }
5959             
5960             cfg.cn.push(this.renderBody());
5961             
5962             if(this.footerShow){
5963                 cfg.cn.push(this.renderFooter());
5964             }
5965             // where does this come from?
5966             //cfg.cls+=  ' TableGrid';
5967         }
5968         
5969         return { cn : [ cfg ] };
5970     },
5971     
5972     initEvents : function()
5973     {   
5974         if(!this.store || !this.cm){
5975             return;
5976         }
5977         if (this.selModel) {
5978             this.selModel.initEvents();
5979         }
5980         
5981         
5982         //Roo.log('initEvents with ds!!!!');
5983         
5984         this.mainBody = this.el.select('tbody', true).first();
5985         this.mainHead = this.el.select('thead', true).first();
5986         
5987         
5988         
5989         
5990         var _this = this;
5991         
5992         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5993             e.on('click', _this.sort, _this);
5994         });
5995         
5996         this.el.on("click", this.onClick, this);
5997         this.el.on("dblclick", this.onDblClick, this);
5998         
5999         // why is this done????? = it breaks dialogs??
6000         //this.parent().el.setStyle('position', 'relative');
6001         
6002         
6003         if (this.footer) {
6004             this.footer.parentId = this.id;
6005             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6006         }
6007         
6008         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6009         
6010         this.store.on('load', this.onLoad, this);
6011         this.store.on('beforeload', this.onBeforeLoad, this);
6012         this.store.on('update', this.onUpdate, this);
6013         this.store.on('add', this.onAdd, this);
6014         this.store.on("clear", this.clear, this);
6015         
6016         this.el.on("contextmenu", this.onContextMenu, this);
6017         
6018         this.mainBody.on('scroll', this.onBodyScroll, this);
6019         
6020         
6021     },
6022     
6023     onContextMenu : function(e, t)
6024     {
6025         this.processEvent("contextmenu", e);
6026     },
6027     
6028     processEvent : function(name, e)
6029     {
6030         if (name != 'touchstart' ) {
6031             this.fireEvent(name, e);    
6032         }
6033         
6034         var t = e.getTarget();
6035         
6036         var cell = Roo.get(t);
6037         
6038         if(!cell){
6039             return;
6040         }
6041         
6042         if(cell.findParent('tfoot', false, true)){
6043             return;
6044         }
6045         
6046         if(cell.findParent('thead', false, true)){
6047             
6048             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6049                 cell = Roo.get(t).findParent('th', false, true);
6050                 if (!cell) {
6051                     Roo.log("failed to find th in thead?");
6052                     Roo.log(e.getTarget());
6053                     return;
6054                 }
6055             }
6056             
6057             var cellIndex = cell.dom.cellIndex;
6058             
6059             var ename = name == 'touchstart' ? 'click' : name;
6060             this.fireEvent("header" + ename, this, cellIndex, e);
6061             
6062             return;
6063         }
6064         
6065         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6066             cell = Roo.get(t).findParent('td', false, true);
6067             if (!cell) {
6068                 Roo.log("failed to find th in tbody?");
6069                 Roo.log(e.getTarget());
6070                 return;
6071             }
6072         }
6073         
6074         var row = cell.findParent('tr', false, true);
6075         var cellIndex = cell.dom.cellIndex;
6076         var rowIndex = row.dom.rowIndex - 1;
6077         
6078         if(row !== false){
6079             
6080             this.fireEvent("row" + name, this, rowIndex, e);
6081             
6082             if(cell !== false){
6083             
6084                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6085             }
6086         }
6087         
6088     },
6089     
6090     onMouseover : function(e, el)
6091     {
6092         var cell = Roo.get(el);
6093         
6094         if(!cell){
6095             return;
6096         }
6097         
6098         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6099             cell = cell.findParent('td', false, true);
6100         }
6101         
6102         var row = cell.findParent('tr', false, true);
6103         var cellIndex = cell.dom.cellIndex;
6104         var rowIndex = row.dom.rowIndex - 1; // start from 0
6105         
6106         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6107         
6108     },
6109     
6110     onMouseout : function(e, el)
6111     {
6112         var cell = Roo.get(el);
6113         
6114         if(!cell){
6115             return;
6116         }
6117         
6118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119             cell = cell.findParent('td', false, true);
6120         }
6121         
6122         var row = cell.findParent('tr', false, true);
6123         var cellIndex = cell.dom.cellIndex;
6124         var rowIndex = row.dom.rowIndex - 1; // start from 0
6125         
6126         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6127         
6128     },
6129     
6130     onClick : function(e, el)
6131     {
6132         var cell = Roo.get(el);
6133         
6134         if(!cell || (!this.cellSelection && !this.rowSelection)){
6135             return;
6136         }
6137         
6138         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6139             cell = cell.findParent('td', false, true);
6140         }
6141         
6142         if(!cell || typeof(cell) == 'undefined'){
6143             return;
6144         }
6145         
6146         var row = cell.findParent('tr', false, true);
6147         
6148         if(!row || typeof(row) == 'undefined'){
6149             return;
6150         }
6151         
6152         var cellIndex = cell.dom.cellIndex;
6153         var rowIndex = this.getRowIndex(row);
6154         
6155         // why??? - should these not be based on SelectionModel?
6156         if(this.cellSelection){
6157             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6158         }
6159         
6160         if(this.rowSelection){
6161             this.fireEvent('rowclick', this, row, rowIndex, e);
6162         }
6163         
6164         
6165     },
6166         
6167     onDblClick : function(e,el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         if(this.cellSelection){
6193             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6194         }
6195         
6196         if(this.rowSelection){
6197             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6198         }
6199     },
6200     
6201     sort : function(e,el)
6202     {
6203         var col = Roo.get(el);
6204         
6205         if(!col.hasClass('sortable')){
6206             return;
6207         }
6208         
6209         var sort = col.attr('sort');
6210         var dir = 'ASC';
6211         
6212         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6213             dir = 'DESC';
6214         }
6215         
6216         this.store.sortInfo = {field : sort, direction : dir};
6217         
6218         if (this.footer) {
6219             Roo.log("calling footer first");
6220             this.footer.onClick('first');
6221         } else {
6222         
6223             this.store.load({ params : { start : 0 } });
6224         }
6225     },
6226     
6227     renderHeader : function()
6228     {
6229         var header = {
6230             tag: 'thead',
6231             cn : []
6232         };
6233         
6234         var cm = this.cm;
6235         this.totalWidth = 0;
6236         
6237         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6238             
6239             var config = cm.config[i];
6240             
6241             var c = {
6242                 tag: 'th',
6243                 style : '',
6244                 html: cm.getColumnHeader(i)
6245             };
6246             
6247             var hh = '';
6248             
6249             if(typeof(config.sortable) != 'undefined' && config.sortable){
6250                 c.cls = 'sortable';
6251                 c.html = '<i class="glyphicon"></i>' + c.html;
6252             }
6253             
6254             if(typeof(config.lgHeader) != 'undefined'){
6255                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6256             }
6257             
6258             if(typeof(config.mdHeader) != 'undefined'){
6259                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6260             }
6261             
6262             if(typeof(config.smHeader) != 'undefined'){
6263                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6264             }
6265             
6266             if(typeof(config.xsHeader) != 'undefined'){
6267                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6268             }
6269             
6270             if(hh.length){
6271                 c.html = hh;
6272             }
6273             
6274             if(typeof(config.tooltip) != 'undefined'){
6275                 c.tooltip = config.tooltip;
6276             }
6277             
6278             if(typeof(config.colspan) != 'undefined'){
6279                 c.colspan = config.colspan;
6280             }
6281             
6282             if(typeof(config.hidden) != 'undefined' && config.hidden){
6283                 c.style += ' display:none;';
6284             }
6285             
6286             if(typeof(config.dataIndex) != 'undefined'){
6287                 c.sort = config.dataIndex;
6288             }
6289             
6290            
6291             
6292             if(typeof(config.align) != 'undefined' && config.align.length){
6293                 c.style += ' text-align:' + config.align + ';';
6294             }
6295             
6296             if(typeof(config.width) != 'undefined'){
6297                 c.style += ' width:' + config.width + 'px;';
6298                 this.totalWidth += config.width;
6299             } else {
6300                 this.totalWidth += 100; // assume minimum of 100 per column?
6301             }
6302             
6303             if(typeof(config.cls) != 'undefined'){
6304                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6305             }
6306             
6307             ['xs','sm','md','lg'].map(function(size){
6308                 
6309                 if(typeof(config[size]) == 'undefined'){
6310                     return;
6311                 }
6312                 
6313                 if (!config[size]) { // 0 = hidden
6314                     c.cls += ' hidden-' + size;
6315                     return;
6316                 }
6317                 
6318                 c.cls += ' col-' + size + '-' + config[size];
6319
6320             });
6321             
6322             header.cn.push(c)
6323         }
6324         
6325         return header;
6326     },
6327     
6328     renderBody : function()
6329     {
6330         var body = {
6331             tag: 'tbody',
6332             cn : [
6333                 {
6334                     tag: 'tr',
6335                     cn : [
6336                         {
6337                             tag : 'td',
6338                             colspan :  this.cm.getColumnCount()
6339                         }
6340                     ]
6341                 }
6342             ]
6343         };
6344         
6345         return body;
6346     },
6347     
6348     renderFooter : function()
6349     {
6350         var footer = {
6351             tag: 'tfoot',
6352             cn : [
6353                 {
6354                     tag: 'tr',
6355                     cn : [
6356                         {
6357                             tag : 'td',
6358                             colspan :  this.cm.getColumnCount()
6359                         }
6360                     ]
6361                 }
6362             ]
6363         };
6364         
6365         return footer;
6366     },
6367     
6368     
6369     
6370     onLoad : function()
6371     {
6372 //        Roo.log('ds onload');
6373         this.clear();
6374         
6375         var _this = this;
6376         var cm = this.cm;
6377         var ds = this.store;
6378         
6379         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6380             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6381             if (_this.store.sortInfo) {
6382                     
6383                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6384                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6385                 }
6386                 
6387                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6388                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6389                 }
6390             }
6391         });
6392         
6393         var tbody =  this.mainBody;
6394               
6395         if(ds.getCount() > 0){
6396             ds.data.each(function(d,rowIndex){
6397                 var row =  this.renderRow(cm, ds, rowIndex);
6398                 
6399                 tbody.createChild(row);
6400                 
6401                 var _this = this;
6402                 
6403                 if(row.cellObjects.length){
6404                     Roo.each(row.cellObjects, function(r){
6405                         _this.renderCellObject(r);
6406                     })
6407                 }
6408                 
6409             }, this);
6410         }
6411         
6412         Roo.each(this.el.select('tbody td', true).elements, function(e){
6413             e.on('mouseover', _this.onMouseover, _this);
6414         });
6415         
6416         Roo.each(this.el.select('tbody td', true).elements, function(e){
6417             e.on('mouseout', _this.onMouseout, _this);
6418         });
6419         this.fireEvent('rowsrendered', this);
6420         //if(this.loadMask){
6421         //    this.maskEl.hide();
6422         //}
6423         
6424         this.autoSize();
6425     },
6426     
6427     
6428     onUpdate : function(ds,record)
6429     {
6430         this.refreshRow(record);
6431         this.autoSize();
6432     },
6433     
6434     onRemove : function(ds, record, index, isUpdate){
6435         if(isUpdate !== true){
6436             this.fireEvent("beforerowremoved", this, index, record);
6437         }
6438         var bt = this.mainBody.dom;
6439         
6440         var rows = this.el.select('tbody > tr', true).elements;
6441         
6442         if(typeof(rows[index]) != 'undefined'){
6443             bt.removeChild(rows[index].dom);
6444         }
6445         
6446 //        if(bt.rows[index]){
6447 //            bt.removeChild(bt.rows[index]);
6448 //        }
6449         
6450         if(isUpdate !== true){
6451             //this.stripeRows(index);
6452             //this.syncRowHeights(index, index);
6453             //this.layout();
6454             this.fireEvent("rowremoved", this, index, record);
6455         }
6456     },
6457     
6458     onAdd : function(ds, records, rowIndex)
6459     {
6460         //Roo.log('on Add called');
6461         // - note this does not handle multiple adding very well..
6462         var bt = this.mainBody.dom;
6463         for (var i =0 ; i < records.length;i++) {
6464             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6465             //Roo.log(records[i]);
6466             //Roo.log(this.store.getAt(rowIndex+i));
6467             this.insertRow(this.store, rowIndex + i, false);
6468             return;
6469         }
6470         
6471     },
6472     
6473     
6474     refreshRow : function(record){
6475         var ds = this.store, index;
6476         if(typeof record == 'number'){
6477             index = record;
6478             record = ds.getAt(index);
6479         }else{
6480             index = ds.indexOf(record);
6481         }
6482         this.insertRow(ds, index, true);
6483         this.autoSize();
6484         this.onRemove(ds, record, index+1, true);
6485         this.autoSize();
6486         //this.syncRowHeights(index, index);
6487         //this.layout();
6488         this.fireEvent("rowupdated", this, index, record);
6489     },
6490     
6491     insertRow : function(dm, rowIndex, isUpdate){
6492         
6493         if(!isUpdate){
6494             this.fireEvent("beforerowsinserted", this, rowIndex);
6495         }
6496             //var s = this.getScrollState();
6497         var row = this.renderRow(this.cm, this.store, rowIndex);
6498         // insert before rowIndex..
6499         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6500         
6501         var _this = this;
6502                 
6503         if(row.cellObjects.length){
6504             Roo.each(row.cellObjects, function(r){
6505                 _this.renderCellObject(r);
6506             })
6507         }
6508             
6509         if(!isUpdate){
6510             this.fireEvent("rowsinserted", this, rowIndex);
6511             //this.syncRowHeights(firstRow, lastRow);
6512             //this.stripeRows(firstRow);
6513             //this.layout();
6514         }
6515         
6516     },
6517     
6518     
6519     getRowDom : function(rowIndex)
6520     {
6521         var rows = this.el.select('tbody > tr', true).elements;
6522         
6523         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6524         
6525     },
6526     // returns the object tree for a tr..
6527   
6528     
6529     renderRow : function(cm, ds, rowIndex) 
6530     {
6531         
6532         var d = ds.getAt(rowIndex);
6533         
6534         var row = {
6535             tag : 'tr',
6536             cn : []
6537         };
6538             
6539         var cellObjects = [];
6540         
6541         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6542             var config = cm.config[i];
6543             
6544             var renderer = cm.getRenderer(i);
6545             var value = '';
6546             var id = false;
6547             
6548             if(typeof(renderer) !== 'undefined'){
6549                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6550             }
6551             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6552             // and are rendered into the cells after the row is rendered - using the id for the element.
6553             
6554             if(typeof(value) === 'object'){
6555                 id = Roo.id();
6556                 cellObjects.push({
6557                     container : id,
6558                     cfg : value 
6559                 })
6560             }
6561             
6562             var rowcfg = {
6563                 record: d,
6564                 rowIndex : rowIndex,
6565                 colIndex : i,
6566                 rowClass : ''
6567             };
6568
6569             this.fireEvent('rowclass', this, rowcfg);
6570             
6571             var td = {
6572                 tag: 'td',
6573                 cls : rowcfg.rowClass,
6574                 style: '',
6575                 html: (typeof(value) === 'object') ? '' : value
6576             };
6577             
6578             if (id) {
6579                 td.id = id;
6580             }
6581             
6582             if(typeof(config.colspan) != 'undefined'){
6583                 td.colspan = config.colspan;
6584             }
6585             
6586             if(typeof(config.hidden) != 'undefined' && config.hidden){
6587                 td.style += ' display:none;';
6588             }
6589             
6590             if(typeof(config.align) != 'undefined' && config.align.length){
6591                 td.style += ' text-align:' + config.align + ';';
6592             }
6593             
6594             if(typeof(config.width) != 'undefined'){
6595                 td.style += ' width:' +  config.width + 'px;';
6596             }
6597             
6598             if(typeof(config.cursor) != 'undefined'){
6599                 td.style += ' cursor:' +  config.cursor + ';';
6600             }
6601             
6602             if(typeof(config.cls) != 'undefined'){
6603                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6604             }
6605             
6606             ['xs','sm','md','lg'].map(function(size){
6607                 
6608                 if(typeof(config[size]) == 'undefined'){
6609                     return;
6610                 }
6611                 
6612                 if (!config[size]) { // 0 = hidden
6613                     td.cls += ' hidden-' + size;
6614                     return;
6615                 }
6616                 
6617                 td.cls += ' col-' + size + '-' + config[size];
6618
6619             });
6620              
6621             row.cn.push(td);
6622            
6623         }
6624         
6625         row.cellObjects = cellObjects;
6626         
6627         return row;
6628           
6629     },
6630     
6631     
6632     
6633     onBeforeLoad : function()
6634     {
6635         //Roo.log('ds onBeforeLoad');
6636         
6637         //this.clear();
6638         
6639         //if(this.loadMask){
6640         //    this.maskEl.show();
6641         //}
6642     },
6643      /**
6644      * Remove all rows
6645      */
6646     clear : function()
6647     {
6648         this.el.select('tbody', true).first().dom.innerHTML = '';
6649     },
6650     /**
6651      * Show or hide a row.
6652      * @param {Number} rowIndex to show or hide
6653      * @param {Boolean} state hide
6654      */
6655     setRowVisibility : function(rowIndex, state)
6656     {
6657         var bt = this.mainBody.dom;
6658         
6659         var rows = this.el.select('tbody > tr', true).elements;
6660         
6661         if(typeof(rows[rowIndex]) == 'undefined'){
6662             return;
6663         }
6664         rows[rowIndex].dom.style.display = state ? '' : 'none';
6665     },
6666     
6667     
6668     getSelectionModel : function(){
6669         if(!this.selModel){
6670             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6671         }
6672         return this.selModel;
6673     },
6674     /*
6675      * Render the Roo.bootstrap object from renderder
6676      */
6677     renderCellObject : function(r)
6678     {
6679         var _this = this;
6680         
6681         var t = r.cfg.render(r.container);
6682         
6683         if(r.cfg.cn){
6684             Roo.each(r.cfg.cn, function(c){
6685                 var child = {
6686                     container: t.getChildContainer(),
6687                     cfg: c
6688                 };
6689                 _this.renderCellObject(child);
6690             })
6691         }
6692     },
6693     
6694     getRowIndex : function(row)
6695     {
6696         var rowIndex = -1;
6697         
6698         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6699             if(el != row){
6700                 return;
6701             }
6702             
6703             rowIndex = index;
6704         });
6705         
6706         return rowIndex;
6707     },
6708      /**
6709      * Returns the grid's underlying element = used by panel.Grid
6710      * @return {Element} The element
6711      */
6712     getGridEl : function(){
6713         return this.el;
6714     },
6715      /**
6716      * Forces a resize - used by panel.Grid
6717      * @return {Element} The element
6718      */
6719     autoSize : function()
6720     {
6721         //var ctr = Roo.get(this.container.dom.parentElement);
6722         var ctr = Roo.get(this.el.dom);
6723         
6724         var thd = this.getGridEl().select('thead',true).first();
6725         var tbd = this.getGridEl().select('tbody', true).first();
6726         
6727         
6728         var cw = ctr.getWidth();
6729         
6730         if (tbd) {
6731             
6732             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6733             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6734             cw -= barsize;
6735         }
6736         cw = Math.max(cw, this.totalWidth);
6737         this.getGridEl().select('tr',true).setWidth(cw);
6738         // resize 'expandable coloumn?
6739         
6740         return; // we doe not have a view in this design..
6741         
6742     },
6743     onBodyScroll: function()
6744     {
6745         
6746         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6747         this.mainHead.setStyle({
6748                     'position' : 'relative',
6749                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6750         });
6751         
6752         
6753     }
6754 });
6755
6756  
6757
6758  /*
6759  * - LGPL
6760  *
6761  * table cell
6762  * 
6763  */
6764
6765 /**
6766  * @class Roo.bootstrap.TableCell
6767  * @extends Roo.bootstrap.Component
6768  * Bootstrap TableCell class
6769  * @cfg {String} html cell contain text
6770  * @cfg {String} cls cell class
6771  * @cfg {String} tag cell tag (td|th) default td
6772  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6773  * @cfg {String} align Aligns the content in a cell
6774  * @cfg {String} axis Categorizes cells
6775  * @cfg {String} bgcolor Specifies the background color of a cell
6776  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6777  * @cfg {Number} colspan Specifies the number of columns a cell should span
6778  * @cfg {String} headers Specifies one or more header cells a cell is related to
6779  * @cfg {Number} height Sets the height of a cell
6780  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6781  * @cfg {Number} rowspan Sets the number of rows a cell should span
6782  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6783  * @cfg {String} valign Vertical aligns the content in a cell
6784  * @cfg {Number} width Specifies the width of a cell
6785  * 
6786  * @constructor
6787  * Create a new TableCell
6788  * @param {Object} config The config object
6789  */
6790
6791 Roo.bootstrap.TableCell = function(config){
6792     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6793 };
6794
6795 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6796     
6797     html: false,
6798     cls: false,
6799     tag: false,
6800     abbr: false,
6801     align: false,
6802     axis: false,
6803     bgcolor: false,
6804     charoff: false,
6805     colspan: false,
6806     headers: false,
6807     height: false,
6808     nowrap: false,
6809     rowspan: false,
6810     scope: false,
6811     valign: false,
6812     width: false,
6813     
6814     
6815     getAutoCreate : function(){
6816         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6817         
6818         cfg = {
6819             tag: 'td'
6820         };
6821         
6822         if(this.tag){
6823             cfg.tag = this.tag;
6824         }
6825         
6826         if (this.html) {
6827             cfg.html=this.html
6828         }
6829         if (this.cls) {
6830             cfg.cls=this.cls
6831         }
6832         if (this.abbr) {
6833             cfg.abbr=this.abbr
6834         }
6835         if (this.align) {
6836             cfg.align=this.align
6837         }
6838         if (this.axis) {
6839             cfg.axis=this.axis
6840         }
6841         if (this.bgcolor) {
6842             cfg.bgcolor=this.bgcolor
6843         }
6844         if (this.charoff) {
6845             cfg.charoff=this.charoff
6846         }
6847         if (this.colspan) {
6848             cfg.colspan=this.colspan
6849         }
6850         if (this.headers) {
6851             cfg.headers=this.headers
6852         }
6853         if (this.height) {
6854             cfg.height=this.height
6855         }
6856         if (this.nowrap) {
6857             cfg.nowrap=this.nowrap
6858         }
6859         if (this.rowspan) {
6860             cfg.rowspan=this.rowspan
6861         }
6862         if (this.scope) {
6863             cfg.scope=this.scope
6864         }
6865         if (this.valign) {
6866             cfg.valign=this.valign
6867         }
6868         if (this.width) {
6869             cfg.width=this.width
6870         }
6871         
6872         
6873         return cfg;
6874     }
6875    
6876 });
6877
6878  
6879
6880  /*
6881  * - LGPL
6882  *
6883  * table row
6884  * 
6885  */
6886
6887 /**
6888  * @class Roo.bootstrap.TableRow
6889  * @extends Roo.bootstrap.Component
6890  * Bootstrap TableRow class
6891  * @cfg {String} cls row class
6892  * @cfg {String} align Aligns the content in a table row
6893  * @cfg {String} bgcolor Specifies a background color for a table row
6894  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6895  * @cfg {String} valign Vertical aligns the content in a table row
6896  * 
6897  * @constructor
6898  * Create a new TableRow
6899  * @param {Object} config The config object
6900  */
6901
6902 Roo.bootstrap.TableRow = function(config){
6903     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6904 };
6905
6906 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6907     
6908     cls: false,
6909     align: false,
6910     bgcolor: false,
6911     charoff: false,
6912     valign: false,
6913     
6914     getAutoCreate : function(){
6915         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6916         
6917         cfg = {
6918             tag: 'tr'
6919         };
6920             
6921         if(this.cls){
6922             cfg.cls = this.cls;
6923         }
6924         if(this.align){
6925             cfg.align = this.align;
6926         }
6927         if(this.bgcolor){
6928             cfg.bgcolor = this.bgcolor;
6929         }
6930         if(this.charoff){
6931             cfg.charoff = this.charoff;
6932         }
6933         if(this.valign){
6934             cfg.valign = this.valign;
6935         }
6936         
6937         return cfg;
6938     }
6939    
6940 });
6941
6942  
6943
6944  /*
6945  * - LGPL
6946  *
6947  * table body
6948  * 
6949  */
6950
6951 /**
6952  * @class Roo.bootstrap.TableBody
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap TableBody class
6955  * @cfg {String} cls element class
6956  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6957  * @cfg {String} align Aligns the content inside the element
6958  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6959  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6960  * 
6961  * @constructor
6962  * Create a new TableBody
6963  * @param {Object} config The config object
6964  */
6965
6966 Roo.bootstrap.TableBody = function(config){
6967     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6968 };
6969
6970 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6971     
6972     cls: false,
6973     tag: false,
6974     align: false,
6975     charoff: false,
6976     valign: false,
6977     
6978     getAutoCreate : function(){
6979         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6980         
6981         cfg = {
6982             tag: 'tbody'
6983         };
6984             
6985         if (this.cls) {
6986             cfg.cls=this.cls
6987         }
6988         if(this.tag){
6989             cfg.tag = this.tag;
6990         }
6991         
6992         if(this.align){
6993             cfg.align = this.align;
6994         }
6995         if(this.charoff){
6996             cfg.charoff = this.charoff;
6997         }
6998         if(this.valign){
6999             cfg.valign = this.valign;
7000         }
7001         
7002         return cfg;
7003     }
7004     
7005     
7006 //    initEvents : function()
7007 //    {
7008 //        
7009 //        if(!this.store){
7010 //            return;
7011 //        }
7012 //        
7013 //        this.store = Roo.factory(this.store, Roo.data);
7014 //        this.store.on('load', this.onLoad, this);
7015 //        
7016 //        this.store.load();
7017 //        
7018 //    },
7019 //    
7020 //    onLoad: function () 
7021 //    {   
7022 //        this.fireEvent('load', this);
7023 //    }
7024 //    
7025 //   
7026 });
7027
7028  
7029
7030  /*
7031  * Based on:
7032  * Ext JS Library 1.1.1
7033  * Copyright(c) 2006-2007, Ext JS, LLC.
7034  *
7035  * Originally Released Under LGPL - original licence link has changed is not relivant.
7036  *
7037  * Fork - LGPL
7038  * <script type="text/javascript">
7039  */
7040
7041 // as we use this in bootstrap.
7042 Roo.namespace('Roo.form');
7043  /**
7044  * @class Roo.form.Action
7045  * Internal Class used to handle form actions
7046  * @constructor
7047  * @param {Roo.form.BasicForm} el The form element or its id
7048  * @param {Object} config Configuration options
7049  */
7050
7051  
7052  
7053 // define the action interface
7054 Roo.form.Action = function(form, options){
7055     this.form = form;
7056     this.options = options || {};
7057 };
7058 /**
7059  * Client Validation Failed
7060  * @const 
7061  */
7062 Roo.form.Action.CLIENT_INVALID = 'client';
7063 /**
7064  * Server Validation Failed
7065  * @const 
7066  */
7067 Roo.form.Action.SERVER_INVALID = 'server';
7068  /**
7069  * Connect to Server Failed
7070  * @const 
7071  */
7072 Roo.form.Action.CONNECT_FAILURE = 'connect';
7073 /**
7074  * Reading Data from Server Failed
7075  * @const 
7076  */
7077 Roo.form.Action.LOAD_FAILURE = 'load';
7078
7079 Roo.form.Action.prototype = {
7080     type : 'default',
7081     failureType : undefined,
7082     response : undefined,
7083     result : undefined,
7084
7085     // interface method
7086     run : function(options){
7087
7088     },
7089
7090     // interface method
7091     success : function(response){
7092
7093     },
7094
7095     // interface method
7096     handleResponse : function(response){
7097
7098     },
7099
7100     // default connection failure
7101     failure : function(response){
7102         
7103         this.response = response;
7104         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7105         this.form.afterAction(this, false);
7106     },
7107
7108     processResponse : function(response){
7109         this.response = response;
7110         if(!response.responseText){
7111             return true;
7112         }
7113         this.result = this.handleResponse(response);
7114         return this.result;
7115     },
7116
7117     // utility functions used internally
7118     getUrl : function(appendParams){
7119         var url = this.options.url || this.form.url || this.form.el.dom.action;
7120         if(appendParams){
7121             var p = this.getParams();
7122             if(p){
7123                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7124             }
7125         }
7126         return url;
7127     },
7128
7129     getMethod : function(){
7130         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7131     },
7132
7133     getParams : function(){
7134         var bp = this.form.baseParams;
7135         var p = this.options.params;
7136         if(p){
7137             if(typeof p == "object"){
7138                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7139             }else if(typeof p == 'string' && bp){
7140                 p += '&' + Roo.urlEncode(bp);
7141             }
7142         }else if(bp){
7143             p = Roo.urlEncode(bp);
7144         }
7145         return p;
7146     },
7147
7148     createCallback : function(){
7149         return {
7150             success: this.success,
7151             failure: this.failure,
7152             scope: this,
7153             timeout: (this.form.timeout*1000),
7154             upload: this.form.fileUpload ? this.success : undefined
7155         };
7156     }
7157 };
7158
7159 Roo.form.Action.Submit = function(form, options){
7160     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7161 };
7162
7163 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7164     type : 'submit',
7165
7166     haveProgress : false,
7167     uploadComplete : false,
7168     
7169     // uploadProgress indicator.
7170     uploadProgress : function()
7171     {
7172         if (!this.form.progressUrl) {
7173             return;
7174         }
7175         
7176         if (!this.haveProgress) {
7177             Roo.MessageBox.progress("Uploading", "Uploading");
7178         }
7179         if (this.uploadComplete) {
7180            Roo.MessageBox.hide();
7181            return;
7182         }
7183         
7184         this.haveProgress = true;
7185    
7186         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7187         
7188         var c = new Roo.data.Connection();
7189         c.request({
7190             url : this.form.progressUrl,
7191             params: {
7192                 id : uid
7193             },
7194             method: 'GET',
7195             success : function(req){
7196                //console.log(data);
7197                 var rdata = false;
7198                 var edata;
7199                 try  {
7200                    rdata = Roo.decode(req.responseText)
7201                 } catch (e) {
7202                     Roo.log("Invalid data from server..");
7203                     Roo.log(edata);
7204                     return;
7205                 }
7206                 if (!rdata || !rdata.success) {
7207                     Roo.log(rdata);
7208                     Roo.MessageBox.alert(Roo.encode(rdata));
7209                     return;
7210                 }
7211                 var data = rdata.data;
7212                 
7213                 if (this.uploadComplete) {
7214                    Roo.MessageBox.hide();
7215                    return;
7216                 }
7217                    
7218                 if (data){
7219                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7220                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7221                     );
7222                 }
7223                 this.uploadProgress.defer(2000,this);
7224             },
7225        
7226             failure: function(data) {
7227                 Roo.log('progress url failed ');
7228                 Roo.log(data);
7229             },
7230             scope : this
7231         });
7232            
7233     },
7234     
7235     
7236     run : function()
7237     {
7238         // run get Values on the form, so it syncs any secondary forms.
7239         this.form.getValues();
7240         
7241         var o = this.options;
7242         var method = this.getMethod();
7243         var isPost = method == 'POST';
7244         if(o.clientValidation === false || this.form.isValid()){
7245             
7246             if (this.form.progressUrl) {
7247                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7248                     (new Date() * 1) + '' + Math.random());
7249                     
7250             } 
7251             
7252             
7253             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7254                 form:this.form.el.dom,
7255                 url:this.getUrl(!isPost),
7256                 method: method,
7257                 params:isPost ? this.getParams() : null,
7258                 isUpload: this.form.fileUpload
7259             }));
7260             
7261             this.uploadProgress();
7262
7263         }else if (o.clientValidation !== false){ // client validation failed
7264             this.failureType = Roo.form.Action.CLIENT_INVALID;
7265             this.form.afterAction(this, false);
7266         }
7267     },
7268
7269     success : function(response)
7270     {
7271         this.uploadComplete= true;
7272         if (this.haveProgress) {
7273             Roo.MessageBox.hide();
7274         }
7275         
7276         
7277         var result = this.processResponse(response);
7278         if(result === true || result.success){
7279             this.form.afterAction(this, true);
7280             return;
7281         }
7282         if(result.errors){
7283             this.form.markInvalid(result.errors);
7284             this.failureType = Roo.form.Action.SERVER_INVALID;
7285         }
7286         this.form.afterAction(this, false);
7287     },
7288     failure : function(response)
7289     {
7290         this.uploadComplete= true;
7291         if (this.haveProgress) {
7292             Roo.MessageBox.hide();
7293         }
7294         
7295         this.response = response;
7296         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7297         this.form.afterAction(this, false);
7298     },
7299     
7300     handleResponse : function(response){
7301         if(this.form.errorReader){
7302             var rs = this.form.errorReader.read(response);
7303             var errors = [];
7304             if(rs.records){
7305                 for(var i = 0, len = rs.records.length; i < len; i++) {
7306                     var r = rs.records[i];
7307                     errors[i] = r.data;
7308                 }
7309             }
7310             if(errors.length < 1){
7311                 errors = null;
7312             }
7313             return {
7314                 success : rs.success,
7315                 errors : errors
7316             };
7317         }
7318         var ret = false;
7319         try {
7320             ret = Roo.decode(response.responseText);
7321         } catch (e) {
7322             ret = {
7323                 success: false,
7324                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7325                 errors : []
7326             };
7327         }
7328         return ret;
7329         
7330     }
7331 });
7332
7333
7334 Roo.form.Action.Load = function(form, options){
7335     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7336     this.reader = this.form.reader;
7337 };
7338
7339 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7340     type : 'load',
7341
7342     run : function(){
7343         
7344         Roo.Ajax.request(Roo.apply(
7345                 this.createCallback(), {
7346                     method:this.getMethod(),
7347                     url:this.getUrl(false),
7348                     params:this.getParams()
7349         }));
7350     },
7351
7352     success : function(response){
7353         
7354         var result = this.processResponse(response);
7355         if(result === true || !result.success || !result.data){
7356             this.failureType = Roo.form.Action.LOAD_FAILURE;
7357             this.form.afterAction(this, false);
7358             return;
7359         }
7360         this.form.clearInvalid();
7361         this.form.setValues(result.data);
7362         this.form.afterAction(this, true);
7363     },
7364
7365     handleResponse : function(response){
7366         if(this.form.reader){
7367             var rs = this.form.reader.read(response);
7368             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7369             return {
7370                 success : rs.success,
7371                 data : data
7372             };
7373         }
7374         return Roo.decode(response.responseText);
7375     }
7376 });
7377
7378 Roo.form.Action.ACTION_TYPES = {
7379     'load' : Roo.form.Action.Load,
7380     'submit' : Roo.form.Action.Submit
7381 };/*
7382  * - LGPL
7383  *
7384  * form
7385  * 
7386  */
7387
7388 /**
7389  * @class Roo.bootstrap.Form
7390  * @extends Roo.bootstrap.Component
7391  * Bootstrap Form class
7392  * @cfg {String} method  GET | POST (default POST)
7393  * @cfg {String} labelAlign top | left (default top)
7394  * @cfg {String} align left  | right - for navbars
7395  * @cfg {Boolean} loadMask load mask when submit (default true)
7396
7397  * 
7398  * @constructor
7399  * Create a new Form
7400  * @param {Object} config The config object
7401  */
7402
7403
7404 Roo.bootstrap.Form = function(config){
7405     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7406     this.addEvents({
7407         /**
7408          * @event clientvalidation
7409          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7410          * @param {Form} this
7411          * @param {Boolean} valid true if the form has passed client-side validation
7412          */
7413         clientvalidation: true,
7414         /**
7415          * @event beforeaction
7416          * Fires before any action is performed. Return false to cancel the action.
7417          * @param {Form} this
7418          * @param {Action} action The action to be performed
7419          */
7420         beforeaction: true,
7421         /**
7422          * @event actionfailed
7423          * Fires when an action fails.
7424          * @param {Form} this
7425          * @param {Action} action The action that failed
7426          */
7427         actionfailed : true,
7428         /**
7429          * @event actioncomplete
7430          * Fires when an action is completed.
7431          * @param {Form} this
7432          * @param {Action} action The action that completed
7433          */
7434         actioncomplete : true
7435     });
7436     
7437 };
7438
7439 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7440       
7441      /**
7442      * @cfg {String} method
7443      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7444      */
7445     method : 'POST',
7446     /**
7447      * @cfg {String} url
7448      * The URL to use for form actions if one isn't supplied in the action options.
7449      */
7450     /**
7451      * @cfg {Boolean} fileUpload
7452      * Set to true if this form is a file upload.
7453      */
7454      
7455     /**
7456      * @cfg {Object} baseParams
7457      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7458      */
7459       
7460     /**
7461      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7462      */
7463     timeout: 30,
7464     /**
7465      * @cfg {Sting} align (left|right) for navbar forms
7466      */
7467     align : 'left',
7468
7469     // private
7470     activeAction : null,
7471  
7472     /**
7473      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7474      * element by passing it or its id or mask the form itself by passing in true.
7475      * @type Mixed
7476      */
7477     waitMsgTarget : false,
7478     
7479     loadMask : true,
7480     
7481     getAutoCreate : function(){
7482         
7483         var cfg = {
7484             tag: 'form',
7485             method : this.method || 'POST',
7486             id : this.id || Roo.id(),
7487             cls : ''
7488         };
7489         if (this.parent().xtype.match(/^Nav/)) {
7490             cfg.cls = 'navbar-form navbar-' + this.align;
7491             
7492         }
7493         
7494         if (this.labelAlign == 'left' ) {
7495             cfg.cls += ' form-horizontal';
7496         }
7497         
7498         
7499         return cfg;
7500     },
7501     initEvents : function()
7502     {
7503         this.el.on('submit', this.onSubmit, this);
7504         // this was added as random key presses on the form where triggering form submit.
7505         this.el.on('keypress', function(e) {
7506             if (e.getCharCode() != 13) {
7507                 return true;
7508             }
7509             // we might need to allow it for textareas.. and some other items.
7510             // check e.getTarget().
7511             
7512             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7513                 return true;
7514             }
7515         
7516             Roo.log("keypress blocked");
7517             
7518             e.preventDefault();
7519             return false;
7520         });
7521         
7522     },
7523     // private
7524     onSubmit : function(e){
7525         e.stopEvent();
7526     },
7527     
7528      /**
7529      * Returns true if client-side validation on the form is successful.
7530      * @return Boolean
7531      */
7532     isValid : function(){
7533         var items = this.getItems();
7534         var valid = true;
7535         items.each(function(f){
7536            if(!f.validate()){
7537                valid = false;
7538                
7539            }
7540         });
7541         return valid;
7542     },
7543     /**
7544      * Returns true if any fields in this form have changed since their original load.
7545      * @return Boolean
7546      */
7547     isDirty : function(){
7548         var dirty = false;
7549         var items = this.getItems();
7550         items.each(function(f){
7551            if(f.isDirty()){
7552                dirty = true;
7553                return false;
7554            }
7555            return true;
7556         });
7557         return dirty;
7558     },
7559      /**
7560      * Performs a predefined action (submit or load) or custom actions you define on this form.
7561      * @param {String} actionName The name of the action type
7562      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7563      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7564      * accept other config options):
7565      * <pre>
7566 Property          Type             Description
7567 ----------------  ---------------  ----------------------------------------------------------------------------------
7568 url               String           The url for the action (defaults to the form's url)
7569 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7570 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7571 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7572                                    validate the form on the client (defaults to false)
7573      * </pre>
7574      * @return {BasicForm} this
7575      */
7576     doAction : function(action, options){
7577         if(typeof action == 'string'){
7578             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7579         }
7580         if(this.fireEvent('beforeaction', this, action) !== false){
7581             this.beforeAction(action);
7582             action.run.defer(100, action);
7583         }
7584         return this;
7585     },
7586     
7587     // private
7588     beforeAction : function(action){
7589         var o = action.options;
7590         
7591         if(this.loadMask){
7592             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7593         }
7594         // not really supported yet.. ??
7595         
7596         //if(this.waitMsgTarget === true){
7597         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7598         //}else if(this.waitMsgTarget){
7599         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7600         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7601         //}else {
7602         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7603        // }
7604          
7605     },
7606
7607     // private
7608     afterAction : function(action, success){
7609         this.activeAction = null;
7610         var o = action.options;
7611         
7612         //if(this.waitMsgTarget === true){
7613             this.el.unmask();
7614         //}else if(this.waitMsgTarget){
7615         //    this.waitMsgTarget.unmask();
7616         //}else{
7617         //    Roo.MessageBox.updateProgress(1);
7618         //    Roo.MessageBox.hide();
7619        // }
7620         // 
7621         if(success){
7622             if(o.reset){
7623                 this.reset();
7624             }
7625             Roo.callback(o.success, o.scope, [this, action]);
7626             this.fireEvent('actioncomplete', this, action);
7627             
7628         }else{
7629             
7630             // failure condition..
7631             // we have a scenario where updates need confirming.
7632             // eg. if a locking scenario exists..
7633             // we look for { errors : { needs_confirm : true }} in the response.
7634             if (
7635                 (typeof(action.result) != 'undefined')  &&
7636                 (typeof(action.result.errors) != 'undefined')  &&
7637                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7638            ){
7639                 var _t = this;
7640                 Roo.log("not supported yet");
7641                  /*
7642                 
7643                 Roo.MessageBox.confirm(
7644                     "Change requires confirmation",
7645                     action.result.errorMsg,
7646                     function(r) {
7647                         if (r != 'yes') {
7648                             return;
7649                         }
7650                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7651                     }
7652                     
7653                 );
7654                 */
7655                 
7656                 
7657                 return;
7658             }
7659             
7660             Roo.callback(o.failure, o.scope, [this, action]);
7661             // show an error message if no failed handler is set..
7662             if (!this.hasListener('actionfailed')) {
7663                 Roo.log("need to add dialog support");
7664                 /*
7665                 Roo.MessageBox.alert("Error",
7666                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7667                         action.result.errorMsg :
7668                         "Saving Failed, please check your entries or try again"
7669                 );
7670                 */
7671             }
7672             
7673             this.fireEvent('actionfailed', this, action);
7674         }
7675         
7676     },
7677     /**
7678      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7679      * @param {String} id The value to search for
7680      * @return Field
7681      */
7682     findField : function(id){
7683         var items = this.getItems();
7684         var field = items.get(id);
7685         if(!field){
7686              items.each(function(f){
7687                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7688                     field = f;
7689                     return false;
7690                 }
7691                 return true;
7692             });
7693         }
7694         return field || null;
7695     },
7696      /**
7697      * Mark fields in this form invalid in bulk.
7698      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7699      * @return {BasicForm} this
7700      */
7701     markInvalid : function(errors){
7702         if(errors instanceof Array){
7703             for(var i = 0, len = errors.length; i < len; i++){
7704                 var fieldError = errors[i];
7705                 var f = this.findField(fieldError.id);
7706                 if(f){
7707                     f.markInvalid(fieldError.msg);
7708                 }
7709             }
7710         }else{
7711             var field, id;
7712             for(id in errors){
7713                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7714                     field.markInvalid(errors[id]);
7715                 }
7716             }
7717         }
7718         //Roo.each(this.childForms || [], function (f) {
7719         //    f.markInvalid(errors);
7720         //});
7721         
7722         return this;
7723     },
7724
7725     /**
7726      * Set values for fields in this form in bulk.
7727      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7728      * @return {BasicForm} this
7729      */
7730     setValues : function(values){
7731         if(values instanceof Array){ // array of objects
7732             for(var i = 0, len = values.length; i < len; i++){
7733                 var v = values[i];
7734                 var f = this.findField(v.id);
7735                 if(f){
7736                     f.setValue(v.value);
7737                     if(this.trackResetOnLoad){
7738                         f.originalValue = f.getValue();
7739                     }
7740                 }
7741             }
7742         }else{ // object hash
7743             var field, id;
7744             for(id in values){
7745                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7746                     
7747                     if (field.setFromData && 
7748                         field.valueField && 
7749                         field.displayField &&
7750                         // combos' with local stores can 
7751                         // be queried via setValue()
7752                         // to set their value..
7753                         (field.store && !field.store.isLocal)
7754                         ) {
7755                         // it's a combo
7756                         var sd = { };
7757                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7758                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7759                         field.setFromData(sd);
7760                         
7761                     } else {
7762                         field.setValue(values[id]);
7763                     }
7764                     
7765                     
7766                     if(this.trackResetOnLoad){
7767                         field.originalValue = field.getValue();
7768                     }
7769                 }
7770             }
7771         }
7772          
7773         //Roo.each(this.childForms || [], function (f) {
7774         //    f.setValues(values);
7775         //});
7776                 
7777         return this;
7778     },
7779
7780     /**
7781      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7782      * they are returned as an array.
7783      * @param {Boolean} asString
7784      * @return {Object}
7785      */
7786     getValues : function(asString){
7787         //if (this.childForms) {
7788             // copy values from the child forms
7789         //    Roo.each(this.childForms, function (f) {
7790         //        this.setValues(f.getValues());
7791         //    }, this);
7792         //}
7793         
7794         
7795         
7796         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7797         if(asString === true){
7798             return fs;
7799         }
7800         return Roo.urlDecode(fs);
7801     },
7802     
7803     /**
7804      * Returns the fields in this form as an object with key/value pairs. 
7805      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7806      * @return {Object}
7807      */
7808     getFieldValues : function(with_hidden)
7809     {
7810         var items = this.getItems();
7811         var ret = {};
7812         items.each(function(f){
7813             if (!f.getName()) {
7814                 return;
7815             }
7816             var v = f.getValue();
7817             if (f.inputType =='radio') {
7818                 if (typeof(ret[f.getName()]) == 'undefined') {
7819                     ret[f.getName()] = ''; // empty..
7820                 }
7821                 
7822                 if (!f.el.dom.checked) {
7823                     return;
7824                     
7825                 }
7826                 v = f.el.dom.value;
7827                 
7828             }
7829             
7830             // not sure if this supported any more..
7831             if ((typeof(v) == 'object') && f.getRawValue) {
7832                 v = f.getRawValue() ; // dates..
7833             }
7834             // combo boxes where name != hiddenName...
7835             if (f.name != f.getName()) {
7836                 ret[f.name] = f.getRawValue();
7837             }
7838             ret[f.getName()] = v;
7839         });
7840         
7841         return ret;
7842     },
7843
7844     /**
7845      * Clears all invalid messages in this form.
7846      * @return {BasicForm} this
7847      */
7848     clearInvalid : function(){
7849         var items = this.getItems();
7850         
7851         items.each(function(f){
7852            f.clearInvalid();
7853         });
7854         
7855         
7856         
7857         return this;
7858     },
7859
7860     /**
7861      * Resets this form.
7862      * @return {BasicForm} this
7863      */
7864     reset : function(){
7865         var items = this.getItems();
7866         items.each(function(f){
7867             f.reset();
7868         });
7869         
7870         Roo.each(this.childForms || [], function (f) {
7871             f.reset();
7872         });
7873        
7874         
7875         return this;
7876     },
7877     getItems : function()
7878     {
7879         var r=new Roo.util.MixedCollection(false, function(o){
7880             return o.id || (o.id = Roo.id());
7881         });
7882         var iter = function(el) {
7883             if (el.inputEl) {
7884                 r.add(el);
7885             }
7886             if (!el.items) {
7887                 return;
7888             }
7889             Roo.each(el.items,function(e) {
7890                 iter(e);
7891             });
7892             
7893             
7894         };
7895         
7896         iter(this);
7897         return r;
7898         
7899         
7900         
7901         
7902     }
7903     
7904 });
7905
7906  
7907 /*
7908  * Based on:
7909  * Ext JS Library 1.1.1
7910  * Copyright(c) 2006-2007, Ext JS, LLC.
7911  *
7912  * Originally Released Under LGPL - original licence link has changed is not relivant.
7913  *
7914  * Fork - LGPL
7915  * <script type="text/javascript">
7916  */
7917 /**
7918  * @class Roo.form.VTypes
7919  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7920  * @singleton
7921  */
7922 Roo.form.VTypes = function(){
7923     // closure these in so they are only created once.
7924     var alpha = /^[a-zA-Z_]+$/;
7925     var alphanum = /^[a-zA-Z0-9_]+$/;
7926     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7927     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7928
7929     // All these messages and functions are configurable
7930     return {
7931         /**
7932          * The function used to validate email addresses
7933          * @param {String} value The email address
7934          */
7935         'email' : function(v){
7936             return email.test(v);
7937         },
7938         /**
7939          * The error text to display when the email validation function returns false
7940          * @type String
7941          */
7942         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7943         /**
7944          * The keystroke filter mask to be applied on email input
7945          * @type RegExp
7946          */
7947         'emailMask' : /[a-z0-9_\.\-@]/i,
7948
7949         /**
7950          * The function used to validate URLs
7951          * @param {String} value The URL
7952          */
7953         'url' : function(v){
7954             return url.test(v);
7955         },
7956         /**
7957          * The error text to display when the url validation function returns false
7958          * @type String
7959          */
7960         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7961         
7962         /**
7963          * The function used to validate alpha values
7964          * @param {String} value The value
7965          */
7966         'alpha' : function(v){
7967             return alpha.test(v);
7968         },
7969         /**
7970          * The error text to display when the alpha validation function returns false
7971          * @type String
7972          */
7973         'alphaText' : 'This field should only contain letters and _',
7974         /**
7975          * The keystroke filter mask to be applied on alpha input
7976          * @type RegExp
7977          */
7978         'alphaMask' : /[a-z_]/i,
7979
7980         /**
7981          * The function used to validate alphanumeric values
7982          * @param {String} value The value
7983          */
7984         'alphanum' : function(v){
7985             return alphanum.test(v);
7986         },
7987         /**
7988          * The error text to display when the alphanumeric validation function returns false
7989          * @type String
7990          */
7991         'alphanumText' : 'This field should only contain letters, numbers and _',
7992         /**
7993          * The keystroke filter mask to be applied on alphanumeric input
7994          * @type RegExp
7995          */
7996         'alphanumMask' : /[a-z0-9_]/i
7997     };
7998 }();/*
7999  * - LGPL
8000  *
8001  * Input
8002  * 
8003  */
8004
8005 /**
8006  * @class Roo.bootstrap.Input
8007  * @extends Roo.bootstrap.Component
8008  * Bootstrap Input class
8009  * @cfg {Boolean} disabled is it disabled
8010  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8011  * @cfg {String} name name of the input
8012  * @cfg {string} fieldLabel - the label associated
8013  * @cfg {string} placeholder - placeholder to put in text.
8014  * @cfg {string}  before - input group add on before
8015  * @cfg {string} after - input group add on after
8016  * @cfg {string} size - (lg|sm) or leave empty..
8017  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8018  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8019  * @cfg {Number} md colspan out of 12 for computer-sized screens
8020  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8021  * @cfg {string} value default value of the input
8022  * @cfg {Number} labelWidth set the width of label (0-12)
8023  * @cfg {String} labelAlign (top|left)
8024  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8025  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8026  * @cfg {String} indicatorpos (left|right) default left
8027
8028  * @cfg {String} align (left|center|right) Default left
8029  * @cfg {Boolean} forceFeedback (true|false) Default false
8030  * 
8031  * 
8032  * 
8033  * 
8034  * @constructor
8035  * Create a new Input
8036  * @param {Object} config The config object
8037  */
8038
8039 Roo.bootstrap.Input = function(config){
8040     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8041    
8042         this.addEvents({
8043             /**
8044              * @event focus
8045              * Fires when this field receives input focus.
8046              * @param {Roo.form.Field} this
8047              */
8048             focus : true,
8049             /**
8050              * @event blur
8051              * Fires when this field loses input focus.
8052              * @param {Roo.form.Field} this
8053              */
8054             blur : true,
8055             /**
8056              * @event specialkey
8057              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8058              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8059              * @param {Roo.form.Field} this
8060              * @param {Roo.EventObject} e The event object
8061              */
8062             specialkey : true,
8063             /**
8064              * @event change
8065              * Fires just before the field blurs if the field value has changed.
8066              * @param {Roo.form.Field} this
8067              * @param {Mixed} newValue The new value
8068              * @param {Mixed} oldValue The original value
8069              */
8070             change : true,
8071             /**
8072              * @event invalid
8073              * Fires after the field has been marked as invalid.
8074              * @param {Roo.form.Field} this
8075              * @param {String} msg The validation message
8076              */
8077             invalid : true,
8078             /**
8079              * @event valid
8080              * Fires after the field has been validated with no errors.
8081              * @param {Roo.form.Field} this
8082              */
8083             valid : true,
8084              /**
8085              * @event keyup
8086              * Fires after the key up
8087              * @param {Roo.form.Field} this
8088              * @param {Roo.EventObject}  e The event Object
8089              */
8090             keyup : true
8091         });
8092 };
8093
8094 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8095      /**
8096      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8097       automatic validation (defaults to "keyup").
8098      */
8099     validationEvent : "keyup",
8100      /**
8101      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8102      */
8103     validateOnBlur : true,
8104     /**
8105      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8106      */
8107     validationDelay : 250,
8108      /**
8109      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8110      */
8111     focusClass : "x-form-focus",  // not needed???
8112     
8113        
8114     /**
8115      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8116      */
8117     invalidClass : "has-warning",
8118     
8119     /**
8120      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8121      */
8122     validClass : "has-success",
8123     
8124     /**
8125      * @cfg {Boolean} hasFeedback (true|false) default true
8126      */
8127     hasFeedback : true,
8128     
8129     /**
8130      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8131      */
8132     invalidFeedbackClass : "glyphicon-warning-sign",
8133     
8134     /**
8135      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8136      */
8137     validFeedbackClass : "glyphicon-ok",
8138     
8139     /**
8140      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8141      */
8142     selectOnFocus : false,
8143     
8144      /**
8145      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8146      */
8147     maskRe : null,
8148        /**
8149      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8150      */
8151     vtype : null,
8152     
8153       /**
8154      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8155      */
8156     disableKeyFilter : false,
8157     
8158        /**
8159      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8160      */
8161     disabled : false,
8162      /**
8163      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8164      */
8165     allowBlank : true,
8166     /**
8167      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8168      */
8169     blankText : "This field is required",
8170     
8171      /**
8172      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8173      */
8174     minLength : 0,
8175     /**
8176      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8177      */
8178     maxLength : Number.MAX_VALUE,
8179     /**
8180      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8181      */
8182     minLengthText : "The minimum length for this field is {0}",
8183     /**
8184      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8185      */
8186     maxLengthText : "The maximum length for this field is {0}",
8187   
8188     
8189     /**
8190      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8191      * If available, this function will be called only after the basic validators all return true, and will be passed the
8192      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8193      */
8194     validator : null,
8195     /**
8196      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8197      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8198      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8199      */
8200     regex : null,
8201     /**
8202      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8203      */
8204     regexText : "",
8205     
8206     autocomplete: false,
8207     
8208     
8209     fieldLabel : '',
8210     inputType : 'text',
8211     
8212     name : false,
8213     placeholder: false,
8214     before : false,
8215     after : false,
8216     size : false,
8217     hasFocus : false,
8218     preventMark: false,
8219     isFormField : true,
8220     value : '',
8221     labelWidth : 2,
8222     labelAlign : false,
8223     readOnly : false,
8224     align : false,
8225     formatedValue : false,
8226     forceFeedback : false,
8227     
8228     indicatorpos : 'left',
8229     
8230     parentLabelAlign : function()
8231     {
8232         var parent = this;
8233         while (parent.parent()) {
8234             parent = parent.parent();
8235             if (typeof(parent.labelAlign) !='undefined') {
8236                 return parent.labelAlign;
8237             }
8238         }
8239         return 'left';
8240         
8241     },
8242     
8243     getAutoCreate : function()
8244     {
8245         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8246         
8247         var id = Roo.id();
8248         
8249         var cfg = {};
8250         
8251         if(this.inputType != 'hidden'){
8252             cfg.cls = 'form-group' //input-group
8253         }
8254         
8255         var input =  {
8256             tag: 'input',
8257             id : id,
8258             type : this.inputType,
8259             value : this.value,
8260             cls : 'form-control',
8261             placeholder : this.placeholder || '',
8262             autocomplete : this.autocomplete || 'new-password'
8263         };
8264         
8265         if(this.align){
8266             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8267         }
8268         
8269         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8270             input.maxLength = this.maxLength;
8271         }
8272         
8273         if (this.disabled) {
8274             input.disabled=true;
8275         }
8276         
8277         if (this.readOnly) {
8278             input.readonly=true;
8279         }
8280         
8281         if (this.name) {
8282             input.name = this.name;
8283         }
8284         
8285         if (this.size) {
8286             input.cls += ' input-' + this.size;
8287         }
8288         
8289         var settings=this;
8290         ['xs','sm','md','lg'].map(function(size){
8291             if (settings[size]) {
8292                 cfg.cls += ' col-' + size + '-' + settings[size];
8293             }
8294         });
8295         
8296         var inputblock = input;
8297         
8298         var feedback = {
8299             tag: 'span',
8300             cls: 'glyphicon form-control-feedback'
8301         };
8302             
8303         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8304             
8305             inputblock = {
8306                 cls : 'has-feedback',
8307                 cn :  [
8308                     input,
8309                     feedback
8310                 ] 
8311             };  
8312         }
8313         
8314         if (this.before || this.after) {
8315             
8316             inputblock = {
8317                 cls : 'input-group',
8318                 cn :  [] 
8319             };
8320             
8321             if (this.before && typeof(this.before) == 'string') {
8322                 
8323                 inputblock.cn.push({
8324                     tag :'span',
8325                     cls : 'roo-input-before input-group-addon',
8326                     html : this.before
8327                 });
8328             }
8329             if (this.before && typeof(this.before) == 'object') {
8330                 this.before = Roo.factory(this.before);
8331                 
8332                 inputblock.cn.push({
8333                     tag :'span',
8334                     cls : 'roo-input-before input-group-' +
8335                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8336                 });
8337             }
8338             
8339             inputblock.cn.push(input);
8340             
8341             if (this.after && typeof(this.after) == 'string') {
8342                 inputblock.cn.push({
8343                     tag :'span',
8344                     cls : 'roo-input-after input-group-addon',
8345                     html : this.after
8346                 });
8347             }
8348             if (this.after && typeof(this.after) == 'object') {
8349                 this.after = Roo.factory(this.after);
8350                 
8351                 inputblock.cn.push({
8352                     tag :'span',
8353                     cls : 'roo-input-after input-group-' +
8354                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8355                 });
8356             }
8357             
8358             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8359                 inputblock.cls += ' has-feedback';
8360                 inputblock.cn.push(feedback);
8361             }
8362         };
8363         
8364         if (align ==='left' && this.fieldLabel.length) {
8365             
8366             cfg.cn = [
8367                 {
8368                     tag : 'i',
8369                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8370                     tooltip : 'This field is required'
8371                 },
8372                 {
8373                     tag: 'label',
8374                     'for' :  id,
8375                     cls : 'control-label col-sm-' + this.labelWidth,
8376                     html : this.fieldLabel
8377
8378                 },
8379                 {
8380                     cls : "col-sm-" + (12 - this.labelWidth), 
8381                     cn: [
8382                         inputblock
8383                     ]
8384                 }
8385
8386             ];
8387             
8388             if(this.indicatorpos == 'right'){
8389                 cfg.cn = [
8390                     {
8391                         tag: 'label',
8392                         'for' :  id,
8393                         cls : 'control-label col-sm-' + this.labelWidth,
8394                         html : this.fieldLabel
8395
8396                     },
8397                     {
8398                         tag : 'i',
8399                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8400                         tooltip : 'This field is required'
8401                     },
8402                     {
8403                         cls : "col-sm-" + (12 - this.labelWidth), 
8404                         cn: [
8405                             inputblock
8406                         ]
8407                     }
8408
8409                 ];
8410             }
8411             
8412         } else if ( this.fieldLabel.length) {
8413                 
8414             cfg.cn = [
8415                 {
8416                     tag : 'i',
8417                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8418                     tooltip : 'This field is required'
8419                 },
8420                 {
8421                     tag: 'label',
8422                    //cls : 'input-group-addon',
8423                     html : this.fieldLabel
8424
8425                 },
8426
8427                inputblock
8428
8429            ];
8430            
8431            if(this.indicatorpos == 'right'){
8432                 
8433                 cfg.cn = [
8434                     {
8435                         tag: 'label',
8436                        //cls : 'input-group-addon',
8437                         html : this.fieldLabel
8438
8439                     },
8440                     {
8441                         tag : 'i',
8442                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8443                         tooltip : 'This field is required'
8444                     },
8445
8446                    inputblock
8447
8448                ];
8449
8450             }
8451
8452         } else {
8453             
8454             cfg.cn = [
8455
8456                     inputblock
8457
8458             ];
8459                 
8460                 
8461         };
8462         
8463         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8464            cfg.cls += ' navbar-form';
8465         }
8466         
8467         if (this.parentType === 'NavGroup') {
8468            cfg.cls += ' navbar-form';
8469            cfg.tag = 'li';
8470         }
8471         
8472         return cfg;
8473         
8474     },
8475     /**
8476      * return the real input element.
8477      */
8478     inputEl: function ()
8479     {
8480         return this.el.select('input.form-control',true).first();
8481     },
8482     
8483     tooltipEl : function()
8484     {
8485         return this.inputEl();
8486     },
8487     
8488     indicatorEl : function()
8489     {
8490         var indicator = this.el.select('i.roo-required-indicator',true).first();
8491         
8492         if(!indicator){
8493             return false;
8494         }
8495         
8496         return indicator;
8497         
8498     },
8499     
8500     setDisabled : function(v)
8501     {
8502         var i  = this.inputEl().dom;
8503         if (!v) {
8504             i.removeAttribute('disabled');
8505             return;
8506             
8507         }
8508         i.setAttribute('disabled','true');
8509     },
8510     initEvents : function()
8511     {
8512           
8513         this.inputEl().on("keydown" , this.fireKey,  this);
8514         this.inputEl().on("focus", this.onFocus,  this);
8515         this.inputEl().on("blur", this.onBlur,  this);
8516         
8517         this.inputEl().relayEvent('keyup', this);
8518         
8519         this.indicator = this.indicatorEl();
8520         
8521         if(this.indicator){
8522             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8523             this.indicator.hide();
8524         }
8525  
8526         // reference to original value for reset
8527         this.originalValue = this.getValue();
8528         //Roo.form.TextField.superclass.initEvents.call(this);
8529         if(this.validationEvent == 'keyup'){
8530             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8531             this.inputEl().on('keyup', this.filterValidation, this);
8532         }
8533         else if(this.validationEvent !== false){
8534             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8535         }
8536         
8537         if(this.selectOnFocus){
8538             this.on("focus", this.preFocus, this);
8539             
8540         }
8541         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8542             this.inputEl().on("keypress", this.filterKeys, this);
8543         } else {
8544             this.inputEl().relayEvent('keypress', this);
8545         }
8546        /* if(this.grow){
8547             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8548             this.el.on("click", this.autoSize,  this);
8549         }
8550         */
8551         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8552             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8553         }
8554         
8555         if (typeof(this.before) == 'object') {
8556             this.before.render(this.el.select('.roo-input-before',true).first());
8557         }
8558         if (typeof(this.after) == 'object') {
8559             this.after.render(this.el.select('.roo-input-after',true).first());
8560         }
8561         
8562         
8563     },
8564     filterValidation : function(e){
8565         if(!e.isNavKeyPress()){
8566             this.validationTask.delay(this.validationDelay);
8567         }
8568     },
8569      /**
8570      * Validates the field value
8571      * @return {Boolean} True if the value is valid, else false
8572      */
8573     validate : function(){
8574         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8575         if(this.disabled || this.validateValue(this.getRawValue())){
8576             this.markValid();
8577             return true;
8578         }
8579         
8580         this.markInvalid();
8581         return false;
8582     },
8583     
8584     
8585     /**
8586      * Validates a value according to the field's validation rules and marks the field as invalid
8587      * if the validation fails
8588      * @param {Mixed} value The value to validate
8589      * @return {Boolean} True if the value is valid, else false
8590      */
8591     validateValue : function(value){
8592         if(value.length < 1)  { // if it's blank
8593             if(this.allowBlank){
8594                 return true;
8595             }
8596             return false;
8597         }
8598         
8599         if(value.length < this.minLength){
8600             return false;
8601         }
8602         if(value.length > this.maxLength){
8603             return false;
8604         }
8605         if(this.vtype){
8606             var vt = Roo.form.VTypes;
8607             if(!vt[this.vtype](value, this)){
8608                 return false;
8609             }
8610         }
8611         if(typeof this.validator == "function"){
8612             var msg = this.validator(value);
8613             if(msg !== true){
8614                 return false;
8615             }
8616         }
8617         
8618         if(this.regex && !this.regex.test(value)){
8619             return false;
8620         }
8621         
8622         return true;
8623     },
8624
8625     
8626     
8627      // private
8628     fireKey : function(e){
8629         //Roo.log('field ' + e.getKey());
8630         if(e.isNavKeyPress()){
8631             this.fireEvent("specialkey", this, e);
8632         }
8633     },
8634     focus : function (selectText){
8635         if(this.rendered){
8636             this.inputEl().focus();
8637             if(selectText === true){
8638                 this.inputEl().dom.select();
8639             }
8640         }
8641         return this;
8642     } ,
8643     
8644     onFocus : function(){
8645         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8646            // this.el.addClass(this.focusClass);
8647         }
8648         if(!this.hasFocus){
8649             this.hasFocus = true;
8650             this.startValue = this.getValue();
8651             this.fireEvent("focus", this);
8652         }
8653     },
8654     
8655     beforeBlur : Roo.emptyFn,
8656
8657     
8658     // private
8659     onBlur : function(){
8660         this.beforeBlur();
8661         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8662             //this.el.removeClass(this.focusClass);
8663         }
8664         this.hasFocus = false;
8665         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8666             this.validate();
8667         }
8668         var v = this.getValue();
8669         if(String(v) !== String(this.startValue)){
8670             this.fireEvent('change', this, v, this.startValue);
8671         }
8672         this.fireEvent("blur", this);
8673     },
8674     
8675     /**
8676      * Resets the current field value to the originally loaded value and clears any validation messages
8677      */
8678     reset : function(){
8679         this.setValue(this.originalValue);
8680         this.validate();
8681     },
8682      /**
8683      * Returns the name of the field
8684      * @return {Mixed} name The name field
8685      */
8686     getName: function(){
8687         return this.name;
8688     },
8689      /**
8690      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8691      * @return {Mixed} value The field value
8692      */
8693     getValue : function(){
8694         
8695         var v = this.inputEl().getValue();
8696         
8697         return v;
8698     },
8699     /**
8700      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8701      * @return {Mixed} value The field value
8702      */
8703     getRawValue : function(){
8704         var v = this.inputEl().getValue();
8705         
8706         return v;
8707     },
8708     
8709     /**
8710      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8711      * @param {Mixed} value The value to set
8712      */
8713     setRawValue : function(v){
8714         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8715     },
8716     
8717     selectText : function(start, end){
8718         var v = this.getRawValue();
8719         if(v.length > 0){
8720             start = start === undefined ? 0 : start;
8721             end = end === undefined ? v.length : end;
8722             var d = this.inputEl().dom;
8723             if(d.setSelectionRange){
8724                 d.setSelectionRange(start, end);
8725             }else if(d.createTextRange){
8726                 var range = d.createTextRange();
8727                 range.moveStart("character", start);
8728                 range.moveEnd("character", v.length-end);
8729                 range.select();
8730             }
8731         }
8732     },
8733     
8734     /**
8735      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8736      * @param {Mixed} value The value to set
8737      */
8738     setValue : function(v){
8739         this.value = v;
8740         if(this.rendered){
8741             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8742             this.validate();
8743         }
8744     },
8745     
8746     /*
8747     processValue : function(value){
8748         if(this.stripCharsRe){
8749             var newValue = value.replace(this.stripCharsRe, '');
8750             if(newValue !== value){
8751                 this.setRawValue(newValue);
8752                 return newValue;
8753             }
8754         }
8755         return value;
8756     },
8757   */
8758     preFocus : function(){
8759         
8760         if(this.selectOnFocus){
8761             this.inputEl().dom.select();
8762         }
8763     },
8764     filterKeys : function(e){
8765         var k = e.getKey();
8766         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8767             return;
8768         }
8769         var c = e.getCharCode(), cc = String.fromCharCode(c);
8770         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8771             return;
8772         }
8773         if(!this.maskRe.test(cc)){
8774             e.stopEvent();
8775         }
8776     },
8777      /**
8778      * Clear any invalid styles/messages for this field
8779      */
8780     clearInvalid : function(){
8781         
8782         if(!this.el || this.preventMark){ // not rendered
8783             return;
8784         }
8785         
8786         if(this.indicator){
8787             this.indicator.hide();
8788         }
8789         
8790         this.el.removeClass(this.invalidClass);
8791         
8792         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8793             
8794             var feedback = this.el.select('.form-control-feedback', true).first();
8795             
8796             if(feedback){
8797                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8798             }
8799             
8800         }
8801         
8802         this.fireEvent('valid', this);
8803     },
8804     
8805      /**
8806      * Mark this field as valid
8807      */
8808     markValid : function()
8809     {
8810         if(!this.el  || this.preventMark){ // not rendered
8811             return;
8812         }
8813         
8814         this.el.removeClass([this.invalidClass, this.validClass]);
8815         
8816         var feedback = this.el.select('.form-control-feedback', true).first();
8817             
8818         if(feedback){
8819             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8820         }
8821
8822         if(this.disabled || this.allowBlank){
8823             return;
8824         }
8825         
8826         if(this.indicator){
8827             this.indicator.hide();
8828         }
8829         
8830         this.el.addClass(this.validClass);
8831         
8832         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8833             
8834             var feedback = this.el.select('.form-control-feedback', true).first();
8835             
8836             if(feedback){
8837                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8838                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8839             }
8840             
8841         }
8842         
8843         this.fireEvent('valid', this);
8844     },
8845     
8846      /**
8847      * Mark this field as invalid
8848      * @param {String} msg The validation message
8849      */
8850     markInvalid : function(msg)
8851     {
8852         if(!this.el  || this.preventMark){ // not rendered
8853             return;
8854         }
8855         
8856         this.el.removeClass([this.invalidClass, this.validClass]);
8857         
8858         var feedback = this.el.select('.form-control-feedback', true).first();
8859             
8860         if(feedback){
8861             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8862         }
8863
8864         if(this.disabled || this.allowBlank){
8865             return;
8866         }
8867         
8868         if(this.indicator){
8869             this.indicator.show();
8870         }
8871         
8872         this.el.addClass(this.invalidClass);
8873         
8874         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8875             
8876             var feedback = this.el.select('.form-control-feedback', true).first();
8877             
8878             if(feedback){
8879                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8880                 
8881                 if(this.getValue().length || this.forceFeedback){
8882                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8883                 }
8884                 
8885             }
8886             
8887         }
8888         
8889         this.fireEvent('invalid', this, msg);
8890     },
8891     // private
8892     SafariOnKeyDown : function(event)
8893     {
8894         // this is a workaround for a password hang bug on chrome/ webkit.
8895         
8896         var isSelectAll = false;
8897         
8898         if(this.inputEl().dom.selectionEnd > 0){
8899             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8900         }
8901         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8902             event.preventDefault();
8903             this.setValue('');
8904             return;
8905         }
8906         
8907         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8908             
8909             event.preventDefault();
8910             // this is very hacky as keydown always get's upper case.
8911             //
8912             var cc = String.fromCharCode(event.getCharCode());
8913             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8914             
8915         }
8916     },
8917     adjustWidth : function(tag, w){
8918         tag = tag.toLowerCase();
8919         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8920             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8921                 if(tag == 'input'){
8922                     return w + 2;
8923                 }
8924                 if(tag == 'textarea'){
8925                     return w-2;
8926                 }
8927             }else if(Roo.isOpera){
8928                 if(tag == 'input'){
8929                     return w + 2;
8930                 }
8931                 if(tag == 'textarea'){
8932                     return w-2;
8933                 }
8934             }
8935         }
8936         return w;
8937     }
8938     
8939 });
8940
8941  
8942 /*
8943  * - LGPL
8944  *
8945  * Input
8946  * 
8947  */
8948
8949 /**
8950  * @class Roo.bootstrap.TextArea
8951  * @extends Roo.bootstrap.Input
8952  * Bootstrap TextArea class
8953  * @cfg {Number} cols Specifies the visible width of a text area
8954  * @cfg {Number} rows Specifies the visible number of lines in a text area
8955  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8956  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8957  * @cfg {string} html text
8958  * 
8959  * @constructor
8960  * Create a new TextArea
8961  * @param {Object} config The config object
8962  */
8963
8964 Roo.bootstrap.TextArea = function(config){
8965     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8966    
8967 };
8968
8969 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8970      
8971     cols : false,
8972     rows : 5,
8973     readOnly : false,
8974     warp : 'soft',
8975     resize : false,
8976     value: false,
8977     html: false,
8978     
8979     getAutoCreate : function(){
8980         
8981         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8982         
8983         var id = Roo.id();
8984         
8985         var cfg = {};
8986         
8987         var input =  {
8988             tag: 'textarea',
8989             id : id,
8990             warp : this.warp,
8991             rows : this.rows,
8992             value : this.value || '',
8993             html: this.html || '',
8994             cls : 'form-control',
8995             placeholder : this.placeholder || '' 
8996             
8997         };
8998         
8999         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9000             input.maxLength = this.maxLength;
9001         }
9002         
9003         if(this.resize){
9004             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9005         }
9006         
9007         if(this.cols){
9008             input.cols = this.cols;
9009         }
9010         
9011         if (this.readOnly) {
9012             input.readonly = true;
9013         }
9014         
9015         if (this.name) {
9016             input.name = this.name;
9017         }
9018         
9019         if (this.size) {
9020             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9021         }
9022         
9023         var settings=this;
9024         ['xs','sm','md','lg'].map(function(size){
9025             if (settings[size]) {
9026                 cfg.cls += ' col-' + size + '-' + settings[size];
9027             }
9028         });
9029         
9030         var inputblock = input;
9031         
9032         if(this.hasFeedback && !this.allowBlank){
9033             
9034             var feedback = {
9035                 tag: 'span',
9036                 cls: 'glyphicon form-control-feedback'
9037             };
9038
9039             inputblock = {
9040                 cls : 'has-feedback',
9041                 cn :  [
9042                     input,
9043                     feedback
9044                 ] 
9045             };  
9046         }
9047         
9048         
9049         if (this.before || this.after) {
9050             
9051             inputblock = {
9052                 cls : 'input-group',
9053                 cn :  [] 
9054             };
9055             if (this.before) {
9056                 inputblock.cn.push({
9057                     tag :'span',
9058                     cls : 'input-group-addon',
9059                     html : this.before
9060                 });
9061             }
9062             
9063             inputblock.cn.push(input);
9064             
9065             if(this.hasFeedback && !this.allowBlank){
9066                 inputblock.cls += ' has-feedback';
9067                 inputblock.cn.push(feedback);
9068             }
9069             
9070             if (this.after) {
9071                 inputblock.cn.push({
9072                     tag :'span',
9073                     cls : 'input-group-addon',
9074                     html : this.after
9075                 });
9076             }
9077             
9078         }
9079         
9080         if (align ==='left' && this.fieldLabel.length) {
9081 //                Roo.log("left and has label");
9082                 cfg.cn = [
9083                     
9084                     {
9085                         tag: 'label',
9086                         'for' :  id,
9087                         cls : 'control-label col-sm-' + this.labelWidth,
9088                         html : this.fieldLabel
9089                         
9090                     },
9091                     {
9092                         cls : "col-sm-" + (12 - this.labelWidth), 
9093                         cn: [
9094                             inputblock
9095                         ]
9096                     }
9097                     
9098                 ];
9099         } else if ( this.fieldLabel.length) {
9100 //                Roo.log(" label");
9101                  cfg.cn = [
9102                    
9103                     {
9104                         tag: 'label',
9105                         //cls : 'input-group-addon',
9106                         html : this.fieldLabel
9107                         
9108                     },
9109                     
9110                     inputblock
9111                     
9112                 ];
9113
9114         } else {
9115             
9116 //                   Roo.log(" no label && no align");
9117                 cfg.cn = [
9118                     
9119                         inputblock
9120                     
9121                 ];
9122                 
9123                 
9124         }
9125         
9126         if (this.disabled) {
9127             input.disabled=true;
9128         }
9129         
9130         return cfg;
9131         
9132     },
9133     /**
9134      * return the real textarea element.
9135      */
9136     inputEl: function ()
9137     {
9138         return this.el.select('textarea.form-control',true).first();
9139     },
9140     
9141     /**
9142      * Clear any invalid styles/messages for this field
9143      */
9144     clearInvalid : function()
9145     {
9146         
9147         if(!this.el || this.preventMark){ // not rendered
9148             return;
9149         }
9150         
9151         var label = this.el.select('label', true).first();
9152         var icon = this.el.select('i.fa-star', true).first();
9153         
9154         if(label && icon){
9155             icon.remove();
9156         }
9157         
9158         this.el.removeClass(this.invalidClass);
9159         
9160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9161             
9162             var feedback = this.el.select('.form-control-feedback', true).first();
9163             
9164             if(feedback){
9165                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9166             }
9167             
9168         }
9169         
9170         this.fireEvent('valid', this);
9171     },
9172     
9173      /**
9174      * Mark this field as valid
9175      */
9176     markValid : function()
9177     {
9178         if(!this.el  || this.preventMark){ // not rendered
9179             return;
9180         }
9181         
9182         this.el.removeClass([this.invalidClass, this.validClass]);
9183         
9184         var feedback = this.el.select('.form-control-feedback', true).first();
9185             
9186         if(feedback){
9187             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9188         }
9189
9190         if(this.disabled || this.allowBlank){
9191             return;
9192         }
9193         
9194         var label = this.el.select('label', true).first();
9195         var icon = this.el.select('i.fa-star', true).first();
9196         
9197         if(label && icon){
9198             icon.remove();
9199         }
9200         
9201         this.el.addClass(this.validClass);
9202         
9203         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9204             
9205             var feedback = this.el.select('.form-control-feedback', true).first();
9206             
9207             if(feedback){
9208                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9209                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9210             }
9211             
9212         }
9213         
9214         this.fireEvent('valid', this);
9215     },
9216     
9217      /**
9218      * Mark this field as invalid
9219      * @param {String} msg The validation message
9220      */
9221     markInvalid : function(msg)
9222     {
9223         if(!this.el  || this.preventMark){ // not rendered
9224             return;
9225         }
9226         
9227         this.el.removeClass([this.invalidClass, this.validClass]);
9228         
9229         var feedback = this.el.select('.form-control-feedback', true).first();
9230             
9231         if(feedback){
9232             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9233         }
9234
9235         if(this.disabled || this.allowBlank){
9236             return;
9237         }
9238         
9239         var label = this.el.select('label', true).first();
9240         var icon = this.el.select('i.fa-star', true).first();
9241         
9242         if(!this.getValue().length && label && !icon){
9243             this.el.createChild({
9244                 tag : 'i',
9245                 cls : 'text-danger fa fa-lg fa-star',
9246                 tooltip : 'This field is required',
9247                 style : 'margin-right:5px;'
9248             }, label, true);
9249         }
9250
9251         this.el.addClass(this.invalidClass);
9252         
9253         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9254             
9255             var feedback = this.el.select('.form-control-feedback', true).first();
9256             
9257             if(feedback){
9258                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9259                 
9260                 if(this.getValue().length || this.forceFeedback){
9261                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9262                 }
9263                 
9264             }
9265             
9266         }
9267         
9268         this.fireEvent('invalid', this, msg);
9269     }
9270 });
9271
9272  
9273 /*
9274  * - LGPL
9275  *
9276  * trigger field - base class for combo..
9277  * 
9278  */
9279  
9280 /**
9281  * @class Roo.bootstrap.TriggerField
9282  * @extends Roo.bootstrap.Input
9283  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9284  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9285  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9286  * for which you can provide a custom implementation.  For example:
9287  * <pre><code>
9288 var trigger = new Roo.bootstrap.TriggerField();
9289 trigger.onTriggerClick = myTriggerFn;
9290 trigger.applyTo('my-field');
9291 </code></pre>
9292  *
9293  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9294  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9295  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9296  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9297  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9298
9299  * @constructor
9300  * Create a new TriggerField.
9301  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9302  * to the base TextField)
9303  */
9304 Roo.bootstrap.TriggerField = function(config){
9305     this.mimicing = false;
9306     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9307 };
9308
9309 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9310     /**
9311      * @cfg {String} triggerClass A CSS class to apply to the trigger
9312      */
9313      /**
9314      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9315      */
9316     hideTrigger:false,
9317
9318     /**
9319      * @cfg {Boolean} removable (true|false) special filter default false
9320      */
9321     removable : false,
9322     
9323     /** @cfg {Boolean} grow @hide */
9324     /** @cfg {Number} growMin @hide */
9325     /** @cfg {Number} growMax @hide */
9326
9327     /**
9328      * @hide 
9329      * @method
9330      */
9331     autoSize: Roo.emptyFn,
9332     // private
9333     monitorTab : true,
9334     // private
9335     deferHeight : true,
9336
9337     
9338     actionMode : 'wrap',
9339     
9340     caret : false,
9341     
9342     
9343     getAutoCreate : function(){
9344        
9345         var align = this.labelAlign || this.parentLabelAlign();
9346         
9347         var id = Roo.id();
9348         
9349         var cfg = {
9350             cls: 'form-group' //input-group
9351         };
9352         
9353         
9354         var input =  {
9355             tag: 'input',
9356             id : id,
9357             type : this.inputType,
9358             cls : 'form-control',
9359             autocomplete: 'new-password',
9360             placeholder : this.placeholder || '' 
9361             
9362         };
9363         if (this.name) {
9364             input.name = this.name;
9365         }
9366         if (this.size) {
9367             input.cls += ' input-' + this.size;
9368         }
9369         
9370         if (this.disabled) {
9371             input.disabled=true;
9372         }
9373         
9374         var inputblock = input;
9375         
9376         if(this.hasFeedback && !this.allowBlank){
9377             
9378             var feedback = {
9379                 tag: 'span',
9380                 cls: 'glyphicon form-control-feedback'
9381             };
9382             
9383             if(this.removable && !this.editable && !this.tickable){
9384                 inputblock = {
9385                     cls : 'has-feedback',
9386                     cn :  [
9387                         inputblock,
9388                         {
9389                             tag: 'button',
9390                             html : 'x',
9391                             cls : 'roo-combo-removable-btn close'
9392                         },
9393                         feedback
9394                     ] 
9395                 };
9396             } else {
9397                 inputblock = {
9398                     cls : 'has-feedback',
9399                     cn :  [
9400                         inputblock,
9401                         feedback
9402                     ] 
9403                 };
9404             }
9405
9406         } else {
9407             if(this.removable && !this.editable && !this.tickable){
9408                 inputblock = {
9409                     cls : 'roo-removable',
9410                     cn :  [
9411                         inputblock,
9412                         {
9413                             tag: 'button',
9414                             html : 'x',
9415                             cls : 'roo-combo-removable-btn close'
9416                         }
9417                     ] 
9418                 };
9419             }
9420         }
9421         
9422         if (this.before || this.after) {
9423             
9424             inputblock = {
9425                 cls : 'input-group',
9426                 cn :  [] 
9427             };
9428             if (this.before) {
9429                 inputblock.cn.push({
9430                     tag :'span',
9431                     cls : 'input-group-addon',
9432                     html : this.before
9433                 });
9434             }
9435             
9436             inputblock.cn.push(input);
9437             
9438             if(this.hasFeedback && !this.allowBlank){
9439                 inputblock.cls += ' has-feedback';
9440                 inputblock.cn.push(feedback);
9441             }
9442             
9443             if (this.after) {
9444                 inputblock.cn.push({
9445                     tag :'span',
9446                     cls : 'input-group-addon',
9447                     html : this.after
9448                 });
9449             }
9450             
9451         };
9452         
9453         var box = {
9454             tag: 'div',
9455             cn: [
9456                 {
9457                     tag: 'input',
9458                     type : 'hidden',
9459                     cls: 'form-hidden-field'
9460                 },
9461                 inputblock
9462             ]
9463             
9464         };
9465         
9466         if(this.multiple){
9467             box = {
9468                 tag: 'div',
9469                 cn: [
9470                     {
9471                         tag: 'input',
9472                         type : 'hidden',
9473                         cls: 'form-hidden-field'
9474                     },
9475                     {
9476                         tag: 'ul',
9477                         cls: 'roo-select2-choices',
9478                         cn:[
9479                             {
9480                                 tag: 'li',
9481                                 cls: 'roo-select2-search-field',
9482                                 cn: [
9483
9484                                     inputblock
9485                                 ]
9486                             }
9487                         ]
9488                     }
9489                 ]
9490             }
9491         };
9492         
9493         var combobox = {
9494             cls: 'roo-select2-container input-group',
9495             cn: [
9496                 box
9497 //                {
9498 //                    tag: 'ul',
9499 //                    cls: 'typeahead typeahead-long dropdown-menu',
9500 //                    style: 'display:none'
9501 //                }
9502             ]
9503         };
9504         
9505         if(!this.multiple && this.showToggleBtn){
9506             
9507             var caret = {
9508                         tag: 'span',
9509                         cls: 'caret'
9510              };
9511             if (this.caret != false) {
9512                 caret = {
9513                      tag: 'i',
9514                      cls: 'fa fa-' + this.caret
9515                 };
9516                 
9517             }
9518             
9519             combobox.cn.push({
9520                 tag :'span',
9521                 cls : 'input-group-addon btn dropdown-toggle',
9522                 cn : [
9523                     caret,
9524                     {
9525                         tag: 'span',
9526                         cls: 'combobox-clear',
9527                         cn  : [
9528                             {
9529                                 tag : 'i',
9530                                 cls: 'icon-remove'
9531                             }
9532                         ]
9533                     }
9534                 ]
9535
9536             })
9537         }
9538         
9539         if(this.multiple){
9540             combobox.cls += ' roo-select2-container-multi';
9541         }
9542         
9543         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9544             
9545 //                Roo.log("left and has label");
9546             cfg.cn = [
9547                 {
9548                     tag : 'i',
9549                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9550                     tooltip : 'This field is required'
9551                 },
9552                 {
9553                     tag: 'label',
9554                     'for' :  id,
9555                     cls : 'control-label col-sm-' + this.labelWidth,
9556                     html : this.fieldLabel
9557
9558                 },
9559                 {
9560                     cls : "col-sm-" + (12 - this.labelWidth), 
9561                     cn: [
9562                         combobox
9563                     ]
9564                 }
9565
9566             ];
9567             
9568             if(this.indicatorpos == 'right'){
9569                 cfg.cn = [
9570                     {
9571                         tag: 'label',
9572                         'for' :  id,
9573                         cls : 'control-label col-sm-' + this.labelWidth,
9574                         html : this.fieldLabel
9575
9576                     },
9577                     {
9578                         tag : 'i',
9579                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9580                         tooltip : 'This field is required'
9581                     },
9582                     {
9583                         cls : "col-sm-" + (12 - this.labelWidth), 
9584                         cn: [
9585                             combobox
9586                         ]
9587                     }
9588
9589                 ];
9590             }
9591             
9592         } else if ( this.fieldLabel.length) {
9593 //                Roo.log(" label");
9594             cfg.cn = [
9595                 {
9596                    tag : 'i',
9597                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9598                    tooltip : 'This field is required'
9599                },
9600                {
9601                    tag: 'label',
9602                    //cls : 'input-group-addon',
9603                    html : this.fieldLabel
9604
9605                },
9606
9607                combobox
9608
9609             ];
9610             
9611             if(this.indicatorpos == 'right'){
9612                 
9613                 cfg.cn = [
9614                     {
9615                        tag: 'label',
9616                        //cls : 'input-group-addon',
9617                        html : this.fieldLabel
9618
9619                     },
9620                     {
9621                        tag : 'i',
9622                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9623                        tooltip : 'This field is required'
9624                     },
9625                     
9626                     combobox
9627
9628                 ];
9629
9630             }
9631
9632         } else {
9633             
9634 //                Roo.log(" no label && no align");
9635                 cfg = combobox
9636                      
9637                 
9638         }
9639          
9640         var settings=this;
9641         ['xs','sm','md','lg'].map(function(size){
9642             if (settings[size]) {
9643                 cfg.cls += ' col-' + size + '-' + settings[size];
9644             }
9645         });
9646         
9647         return cfg;
9648         
9649     },
9650     
9651     
9652     
9653     // private
9654     onResize : function(w, h){
9655 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9656 //        if(typeof w == 'number'){
9657 //            var x = w - this.trigger.getWidth();
9658 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9659 //            this.trigger.setStyle('left', x+'px');
9660 //        }
9661     },
9662
9663     // private
9664     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9665
9666     // private
9667     getResizeEl : function(){
9668         return this.inputEl();
9669     },
9670
9671     // private
9672     getPositionEl : function(){
9673         return this.inputEl();
9674     },
9675
9676     // private
9677     alignErrorIcon : function(){
9678         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9679     },
9680
9681     // private
9682     initEvents : function(){
9683         
9684         this.createList();
9685         
9686         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9687         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9688         if(!this.multiple && this.showToggleBtn){
9689             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9690             if(this.hideTrigger){
9691                 this.trigger.setDisplayed(false);
9692             }
9693             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9694         }
9695         
9696         if(this.multiple){
9697             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9698         }
9699         
9700         if(this.removable && !this.editable && !this.tickable){
9701             var close = this.closeTriggerEl();
9702             
9703             if(close){
9704                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9705                 close.on('click', this.removeBtnClick, this, close);
9706             }
9707         }
9708         
9709         //this.trigger.addClassOnOver('x-form-trigger-over');
9710         //this.trigger.addClassOnClick('x-form-trigger-click');
9711         
9712         //if(!this.width){
9713         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9714         //}
9715     },
9716     
9717     closeTriggerEl : function()
9718     {
9719         var close = this.el.select('.roo-combo-removable-btn', true).first();
9720         return close ? close : false;
9721     },
9722     
9723     removeBtnClick : function(e, h, el)
9724     {
9725         e.preventDefault();
9726         
9727         if(this.fireEvent("remove", this) !== false){
9728             this.reset();
9729             this.fireEvent("afterremove", this)
9730         }
9731     },
9732     
9733     createList : function()
9734     {
9735         this.list = Roo.get(document.body).createChild({
9736             tag: 'ul',
9737             cls: 'typeahead typeahead-long dropdown-menu',
9738             style: 'display:none'
9739         });
9740         
9741         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9742         
9743     },
9744
9745     // private
9746     initTrigger : function(){
9747        
9748     },
9749
9750     // private
9751     onDestroy : function(){
9752         if(this.trigger){
9753             this.trigger.removeAllListeners();
9754           //  this.trigger.remove();
9755         }
9756         //if(this.wrap){
9757         //    this.wrap.remove();
9758         //}
9759         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9760     },
9761
9762     // private
9763     onFocus : function(){
9764         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9765         /*
9766         if(!this.mimicing){
9767             this.wrap.addClass('x-trigger-wrap-focus');
9768             this.mimicing = true;
9769             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9770             if(this.monitorTab){
9771                 this.el.on("keydown", this.checkTab, this);
9772             }
9773         }
9774         */
9775     },
9776
9777     // private
9778     checkTab : function(e){
9779         if(e.getKey() == e.TAB){
9780             this.triggerBlur();
9781         }
9782     },
9783
9784     // private
9785     onBlur : function(){
9786         // do nothing
9787     },
9788
9789     // private
9790     mimicBlur : function(e, t){
9791         /*
9792         if(!this.wrap.contains(t) && this.validateBlur()){
9793             this.triggerBlur();
9794         }
9795         */
9796     },
9797
9798     // private
9799     triggerBlur : function(){
9800         this.mimicing = false;
9801         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9802         if(this.monitorTab){
9803             this.el.un("keydown", this.checkTab, this);
9804         }
9805         //this.wrap.removeClass('x-trigger-wrap-focus');
9806         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9807     },
9808
9809     // private
9810     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9811     validateBlur : function(e, t){
9812         return true;
9813     },
9814
9815     // private
9816     onDisable : function(){
9817         this.inputEl().dom.disabled = true;
9818         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9819         //if(this.wrap){
9820         //    this.wrap.addClass('x-item-disabled');
9821         //}
9822     },
9823
9824     // private
9825     onEnable : function(){
9826         this.inputEl().dom.disabled = false;
9827         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9828         //if(this.wrap){
9829         //    this.el.removeClass('x-item-disabled');
9830         //}
9831     },
9832
9833     // private
9834     onShow : function(){
9835         var ae = this.getActionEl();
9836         
9837         if(ae){
9838             ae.dom.style.display = '';
9839             ae.dom.style.visibility = 'visible';
9840         }
9841     },
9842
9843     // private
9844     
9845     onHide : function(){
9846         var ae = this.getActionEl();
9847         ae.dom.style.display = 'none';
9848     },
9849
9850     /**
9851      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9852      * by an implementing function.
9853      * @method
9854      * @param {EventObject} e
9855      */
9856     onTriggerClick : Roo.emptyFn
9857 });
9858  /*
9859  * Based on:
9860  * Ext JS Library 1.1.1
9861  * Copyright(c) 2006-2007, Ext JS, LLC.
9862  *
9863  * Originally Released Under LGPL - original licence link has changed is not relivant.
9864  *
9865  * Fork - LGPL
9866  * <script type="text/javascript">
9867  */
9868
9869
9870 /**
9871  * @class Roo.data.SortTypes
9872  * @singleton
9873  * Defines the default sorting (casting?) comparison functions used when sorting data.
9874  */
9875 Roo.data.SortTypes = {
9876     /**
9877      * Default sort that does nothing
9878      * @param {Mixed} s The value being converted
9879      * @return {Mixed} The comparison value
9880      */
9881     none : function(s){
9882         return s;
9883     },
9884     
9885     /**
9886      * The regular expression used to strip tags
9887      * @type {RegExp}
9888      * @property
9889      */
9890     stripTagsRE : /<\/?[^>]+>/gi,
9891     
9892     /**
9893      * Strips all HTML tags to sort on text only
9894      * @param {Mixed} s The value being converted
9895      * @return {String} The comparison value
9896      */
9897     asText : function(s){
9898         return String(s).replace(this.stripTagsRE, "");
9899     },
9900     
9901     /**
9902      * Strips all HTML tags to sort on text only - Case insensitive
9903      * @param {Mixed} s The value being converted
9904      * @return {String} The comparison value
9905      */
9906     asUCText : function(s){
9907         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9908     },
9909     
9910     /**
9911      * Case insensitive string
9912      * @param {Mixed} s The value being converted
9913      * @return {String} The comparison value
9914      */
9915     asUCString : function(s) {
9916         return String(s).toUpperCase();
9917     },
9918     
9919     /**
9920      * Date sorting
9921      * @param {Mixed} s The value being converted
9922      * @return {Number} The comparison value
9923      */
9924     asDate : function(s) {
9925         if(!s){
9926             return 0;
9927         }
9928         if(s instanceof Date){
9929             return s.getTime();
9930         }
9931         return Date.parse(String(s));
9932     },
9933     
9934     /**
9935      * Float sorting
9936      * @param {Mixed} s The value being converted
9937      * @return {Float} The comparison value
9938      */
9939     asFloat : function(s) {
9940         var val = parseFloat(String(s).replace(/,/g, ""));
9941         if(isNaN(val)) {
9942             val = 0;
9943         }
9944         return val;
9945     },
9946     
9947     /**
9948      * Integer sorting
9949      * @param {Mixed} s The value being converted
9950      * @return {Number} The comparison value
9951      */
9952     asInt : function(s) {
9953         var val = parseInt(String(s).replace(/,/g, ""));
9954         if(isNaN(val)) {
9955             val = 0;
9956         }
9957         return val;
9958     }
9959 };/*
9960  * Based on:
9961  * Ext JS Library 1.1.1
9962  * Copyright(c) 2006-2007, Ext JS, LLC.
9963  *
9964  * Originally Released Under LGPL - original licence link has changed is not relivant.
9965  *
9966  * Fork - LGPL
9967  * <script type="text/javascript">
9968  */
9969
9970 /**
9971 * @class Roo.data.Record
9972  * Instances of this class encapsulate both record <em>definition</em> information, and record
9973  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9974  * to access Records cached in an {@link Roo.data.Store} object.<br>
9975  * <p>
9976  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9977  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9978  * objects.<br>
9979  * <p>
9980  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9981  * @constructor
9982  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9983  * {@link #create}. The parameters are the same.
9984  * @param {Array} data An associative Array of data values keyed by the field name.
9985  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9986  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9987  * not specified an integer id is generated.
9988  */
9989 Roo.data.Record = function(data, id){
9990     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9991     this.data = data;
9992 };
9993
9994 /**
9995  * Generate a constructor for a specific record layout.
9996  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9997  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9998  * Each field definition object may contain the following properties: <ul>
9999  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10000  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10001  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10002  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10003  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10004  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10005  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10006  * this may be omitted.</p></li>
10007  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10008  * <ul><li>auto (Default, implies no conversion)</li>
10009  * <li>string</li>
10010  * <li>int</li>
10011  * <li>float</li>
10012  * <li>boolean</li>
10013  * <li>date</li></ul></p></li>
10014  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10015  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10016  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10017  * by the Reader into an object that will be stored in the Record. It is passed the
10018  * following parameters:<ul>
10019  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10020  * </ul></p></li>
10021  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10022  * </ul>
10023  * <br>usage:<br><pre><code>
10024 var TopicRecord = Roo.data.Record.create(
10025     {name: 'title', mapping: 'topic_title'},
10026     {name: 'author', mapping: 'username'},
10027     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10028     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10029     {name: 'lastPoster', mapping: 'user2'},
10030     {name: 'excerpt', mapping: 'post_text'}
10031 );
10032
10033 var myNewRecord = new TopicRecord({
10034     title: 'Do my job please',
10035     author: 'noobie',
10036     totalPosts: 1,
10037     lastPost: new Date(),
10038     lastPoster: 'Animal',
10039     excerpt: 'No way dude!'
10040 });
10041 myStore.add(myNewRecord);
10042 </code></pre>
10043  * @method create
10044  * @static
10045  */
10046 Roo.data.Record.create = function(o){
10047     var f = function(){
10048         f.superclass.constructor.apply(this, arguments);
10049     };
10050     Roo.extend(f, Roo.data.Record);
10051     var p = f.prototype;
10052     p.fields = new Roo.util.MixedCollection(false, function(field){
10053         return field.name;
10054     });
10055     for(var i = 0, len = o.length; i < len; i++){
10056         p.fields.add(new Roo.data.Field(o[i]));
10057     }
10058     f.getField = function(name){
10059         return p.fields.get(name);  
10060     };
10061     return f;
10062 };
10063
10064 Roo.data.Record.AUTO_ID = 1000;
10065 Roo.data.Record.EDIT = 'edit';
10066 Roo.data.Record.REJECT = 'reject';
10067 Roo.data.Record.COMMIT = 'commit';
10068
10069 Roo.data.Record.prototype = {
10070     /**
10071      * Readonly flag - true if this record has been modified.
10072      * @type Boolean
10073      */
10074     dirty : false,
10075     editing : false,
10076     error: null,
10077     modified: null,
10078
10079     // private
10080     join : function(store){
10081         this.store = store;
10082     },
10083
10084     /**
10085      * Set the named field to the specified value.
10086      * @param {String} name The name of the field to set.
10087      * @param {Object} value The value to set the field to.
10088      */
10089     set : function(name, value){
10090         if(this.data[name] == value){
10091             return;
10092         }
10093         this.dirty = true;
10094         if(!this.modified){
10095             this.modified = {};
10096         }
10097         if(typeof this.modified[name] == 'undefined'){
10098             this.modified[name] = this.data[name];
10099         }
10100         this.data[name] = value;
10101         if(!this.editing && this.store){
10102             this.store.afterEdit(this);
10103         }       
10104     },
10105
10106     /**
10107      * Get the value of the named field.
10108      * @param {String} name The name of the field to get the value of.
10109      * @return {Object} The value of the field.
10110      */
10111     get : function(name){
10112         return this.data[name]; 
10113     },
10114
10115     // private
10116     beginEdit : function(){
10117         this.editing = true;
10118         this.modified = {}; 
10119     },
10120
10121     // private
10122     cancelEdit : function(){
10123         this.editing = false;
10124         delete this.modified;
10125     },
10126
10127     // private
10128     endEdit : function(){
10129         this.editing = false;
10130         if(this.dirty && this.store){
10131             this.store.afterEdit(this);
10132         }
10133     },
10134
10135     /**
10136      * Usually called by the {@link Roo.data.Store} which owns the Record.
10137      * Rejects all changes made to the Record since either creation, or the last commit operation.
10138      * Modified fields are reverted to their original values.
10139      * <p>
10140      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10141      * of reject operations.
10142      */
10143     reject : function(){
10144         var m = this.modified;
10145         for(var n in m){
10146             if(typeof m[n] != "function"){
10147                 this.data[n] = m[n];
10148             }
10149         }
10150         this.dirty = false;
10151         delete this.modified;
10152         this.editing = false;
10153         if(this.store){
10154             this.store.afterReject(this);
10155         }
10156     },
10157
10158     /**
10159      * Usually called by the {@link Roo.data.Store} which owns the Record.
10160      * Commits all changes made to the Record since either creation, or the last commit operation.
10161      * <p>
10162      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10163      * of commit operations.
10164      */
10165     commit : function(){
10166         this.dirty = false;
10167         delete this.modified;
10168         this.editing = false;
10169         if(this.store){
10170             this.store.afterCommit(this);
10171         }
10172     },
10173
10174     // private
10175     hasError : function(){
10176         return this.error != null;
10177     },
10178
10179     // private
10180     clearError : function(){
10181         this.error = null;
10182     },
10183
10184     /**
10185      * Creates a copy of this record.
10186      * @param {String} id (optional) A new record id if you don't want to use this record's id
10187      * @return {Record}
10188      */
10189     copy : function(newId) {
10190         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10191     }
10192 };/*
10193  * Based on:
10194  * Ext JS Library 1.1.1
10195  * Copyright(c) 2006-2007, Ext JS, LLC.
10196  *
10197  * Originally Released Under LGPL - original licence link has changed is not relivant.
10198  *
10199  * Fork - LGPL
10200  * <script type="text/javascript">
10201  */
10202
10203
10204
10205 /**
10206  * @class Roo.data.Store
10207  * @extends Roo.util.Observable
10208  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10209  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10210  * <p>
10211  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10212  * has no knowledge of the format of the data returned by the Proxy.<br>
10213  * <p>
10214  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10215  * instances from the data object. These records are cached and made available through accessor functions.
10216  * @constructor
10217  * Creates a new Store.
10218  * @param {Object} config A config object containing the objects needed for the Store to access data,
10219  * and read the data into Records.
10220  */
10221 Roo.data.Store = function(config){
10222     this.data = new Roo.util.MixedCollection(false);
10223     this.data.getKey = function(o){
10224         return o.id;
10225     };
10226     this.baseParams = {};
10227     // private
10228     this.paramNames = {
10229         "start" : "start",
10230         "limit" : "limit",
10231         "sort" : "sort",
10232         "dir" : "dir",
10233         "multisort" : "_multisort"
10234     };
10235
10236     if(config && config.data){
10237         this.inlineData = config.data;
10238         delete config.data;
10239     }
10240
10241     Roo.apply(this, config);
10242     
10243     if(this.reader){ // reader passed
10244         this.reader = Roo.factory(this.reader, Roo.data);
10245         this.reader.xmodule = this.xmodule || false;
10246         if(!this.recordType){
10247             this.recordType = this.reader.recordType;
10248         }
10249         if(this.reader.onMetaChange){
10250             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10251         }
10252     }
10253
10254     if(this.recordType){
10255         this.fields = this.recordType.prototype.fields;
10256     }
10257     this.modified = [];
10258
10259     this.addEvents({
10260         /**
10261          * @event datachanged
10262          * Fires when the data cache has changed, and a widget which is using this Store
10263          * as a Record cache should refresh its view.
10264          * @param {Store} this
10265          */
10266         datachanged : true,
10267         /**
10268          * @event metachange
10269          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10270          * @param {Store} this
10271          * @param {Object} meta The JSON metadata
10272          */
10273         metachange : true,
10274         /**
10275          * @event add
10276          * Fires when Records have been added to the Store
10277          * @param {Store} this
10278          * @param {Roo.data.Record[]} records The array of Records added
10279          * @param {Number} index The index at which the record(s) were added
10280          */
10281         add : true,
10282         /**
10283          * @event remove
10284          * Fires when a Record has been removed from the Store
10285          * @param {Store} this
10286          * @param {Roo.data.Record} record The Record that was removed
10287          * @param {Number} index The index at which the record was removed
10288          */
10289         remove : true,
10290         /**
10291          * @event update
10292          * Fires when a Record has been updated
10293          * @param {Store} this
10294          * @param {Roo.data.Record} record The Record that was updated
10295          * @param {String} operation The update operation being performed.  Value may be one of:
10296          * <pre><code>
10297  Roo.data.Record.EDIT
10298  Roo.data.Record.REJECT
10299  Roo.data.Record.COMMIT
10300          * </code></pre>
10301          */
10302         update : true,
10303         /**
10304          * @event clear
10305          * Fires when the data cache has been cleared.
10306          * @param {Store} this
10307          */
10308         clear : true,
10309         /**
10310          * @event beforeload
10311          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10312          * the load action will be canceled.
10313          * @param {Store} this
10314          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10315          */
10316         beforeload : true,
10317         /**
10318          * @event beforeloadadd
10319          * Fires after a new set of Records has been loaded.
10320          * @param {Store} this
10321          * @param {Roo.data.Record[]} records The Records that were loaded
10322          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10323          */
10324         beforeloadadd : true,
10325         /**
10326          * @event load
10327          * Fires after a new set of Records has been loaded, before they are added to the store.
10328          * @param {Store} this
10329          * @param {Roo.data.Record[]} records The Records that were loaded
10330          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10331          * @params {Object} return from reader
10332          */
10333         load : true,
10334         /**
10335          * @event loadexception
10336          * Fires if an exception occurs in the Proxy during loading.
10337          * Called with the signature of the Proxy's "loadexception" event.
10338          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10339          * 
10340          * @param {Proxy} 
10341          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10342          * @param {Object} load options 
10343          * @param {Object} jsonData from your request (normally this contains the Exception)
10344          */
10345         loadexception : true
10346     });
10347     
10348     if(this.proxy){
10349         this.proxy = Roo.factory(this.proxy, Roo.data);
10350         this.proxy.xmodule = this.xmodule || false;
10351         this.relayEvents(this.proxy,  ["loadexception"]);
10352     }
10353     this.sortToggle = {};
10354     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10355
10356     Roo.data.Store.superclass.constructor.call(this);
10357
10358     if(this.inlineData){
10359         this.loadData(this.inlineData);
10360         delete this.inlineData;
10361     }
10362 };
10363
10364 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10365      /**
10366     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10367     * without a remote query - used by combo/forms at present.
10368     */
10369     
10370     /**
10371     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10372     */
10373     /**
10374     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10375     */
10376     /**
10377     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10378     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10379     */
10380     /**
10381     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10382     * on any HTTP request
10383     */
10384     /**
10385     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10386     */
10387     /**
10388     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10389     */
10390     multiSort: false,
10391     /**
10392     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10393     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10394     */
10395     remoteSort : false,
10396
10397     /**
10398     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10399      * loaded or when a record is removed. (defaults to false).
10400     */
10401     pruneModifiedRecords : false,
10402
10403     // private
10404     lastOptions : null,
10405
10406     /**
10407      * Add Records to the Store and fires the add event.
10408      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10409      */
10410     add : function(records){
10411         records = [].concat(records);
10412         for(var i = 0, len = records.length; i < len; i++){
10413             records[i].join(this);
10414         }
10415         var index = this.data.length;
10416         this.data.addAll(records);
10417         this.fireEvent("add", this, records, index);
10418     },
10419
10420     /**
10421      * Remove a Record from the Store and fires the remove event.
10422      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10423      */
10424     remove : function(record){
10425         var index = this.data.indexOf(record);
10426         this.data.removeAt(index);
10427         if(this.pruneModifiedRecords){
10428             this.modified.remove(record);
10429         }
10430         this.fireEvent("remove", this, record, index);
10431     },
10432
10433     /**
10434      * Remove all Records from the Store and fires the clear event.
10435      */
10436     removeAll : function(){
10437         this.data.clear();
10438         if(this.pruneModifiedRecords){
10439             this.modified = [];
10440         }
10441         this.fireEvent("clear", this);
10442     },
10443
10444     /**
10445      * Inserts Records to the Store at the given index and fires the add event.
10446      * @param {Number} index The start index at which to insert the passed Records.
10447      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10448      */
10449     insert : function(index, records){
10450         records = [].concat(records);
10451         for(var i = 0, len = records.length; i < len; i++){
10452             this.data.insert(index, records[i]);
10453             records[i].join(this);
10454         }
10455         this.fireEvent("add", this, records, index);
10456     },
10457
10458     /**
10459      * Get the index within the cache of the passed Record.
10460      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10461      * @return {Number} The index of the passed Record. Returns -1 if not found.
10462      */
10463     indexOf : function(record){
10464         return this.data.indexOf(record);
10465     },
10466
10467     /**
10468      * Get the index within the cache of the Record with the passed id.
10469      * @param {String} id The id of the Record to find.
10470      * @return {Number} The index of the Record. Returns -1 if not found.
10471      */
10472     indexOfId : function(id){
10473         return this.data.indexOfKey(id);
10474     },
10475
10476     /**
10477      * Get the Record with the specified id.
10478      * @param {String} id The id of the Record to find.
10479      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10480      */
10481     getById : function(id){
10482         return this.data.key(id);
10483     },
10484
10485     /**
10486      * Get the Record at the specified index.
10487      * @param {Number} index The index of the Record to find.
10488      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10489      */
10490     getAt : function(index){
10491         return this.data.itemAt(index);
10492     },
10493
10494     /**
10495      * Returns a range of Records between specified indices.
10496      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10497      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10498      * @return {Roo.data.Record[]} An array of Records
10499      */
10500     getRange : function(start, end){
10501         return this.data.getRange(start, end);
10502     },
10503
10504     // private
10505     storeOptions : function(o){
10506         o = Roo.apply({}, o);
10507         delete o.callback;
10508         delete o.scope;
10509         this.lastOptions = o;
10510     },
10511
10512     /**
10513      * Loads the Record cache from the configured Proxy using the configured Reader.
10514      * <p>
10515      * If using remote paging, then the first load call must specify the <em>start</em>
10516      * and <em>limit</em> properties in the options.params property to establish the initial
10517      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10518      * <p>
10519      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10520      * and this call will return before the new data has been loaded. Perform any post-processing
10521      * in a callback function, or in a "load" event handler.</strong>
10522      * <p>
10523      * @param {Object} options An object containing properties which control loading options:<ul>
10524      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10525      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10526      * passed the following arguments:<ul>
10527      * <li>r : Roo.data.Record[]</li>
10528      * <li>options: Options object from the load call</li>
10529      * <li>success: Boolean success indicator</li></ul></li>
10530      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10531      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10532      * </ul>
10533      */
10534     load : function(options){
10535         options = options || {};
10536         if(this.fireEvent("beforeload", this, options) !== false){
10537             this.storeOptions(options);
10538             var p = Roo.apply(options.params || {}, this.baseParams);
10539             // if meta was not loaded from remote source.. try requesting it.
10540             if (!this.reader.metaFromRemote) {
10541                 p._requestMeta = 1;
10542             }
10543             if(this.sortInfo && this.remoteSort){
10544                 var pn = this.paramNames;
10545                 p[pn["sort"]] = this.sortInfo.field;
10546                 p[pn["dir"]] = this.sortInfo.direction;
10547             }
10548             if (this.multiSort) {
10549                 var pn = this.paramNames;
10550                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10551             }
10552             
10553             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10554         }
10555     },
10556
10557     /**
10558      * Reloads the Record cache from the configured Proxy using the configured Reader and
10559      * the options from the last load operation performed.
10560      * @param {Object} options (optional) An object containing properties which may override the options
10561      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10562      * the most recently used options are reused).
10563      */
10564     reload : function(options){
10565         this.load(Roo.applyIf(options||{}, this.lastOptions));
10566     },
10567
10568     // private
10569     // Called as a callback by the Reader during a load operation.
10570     loadRecords : function(o, options, success){
10571         if(!o || success === false){
10572             if(success !== false){
10573                 this.fireEvent("load", this, [], options, o);
10574             }
10575             if(options.callback){
10576                 options.callback.call(options.scope || this, [], options, false);
10577             }
10578             return;
10579         }
10580         // if data returned failure - throw an exception.
10581         if (o.success === false) {
10582             // show a message if no listener is registered.
10583             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10584                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10585             }
10586             // loadmask wil be hooked into this..
10587             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10588             return;
10589         }
10590         var r = o.records, t = o.totalRecords || r.length;
10591         
10592         this.fireEvent("beforeloadadd", this, r, options, o);
10593         
10594         if(!options || options.add !== true){
10595             if(this.pruneModifiedRecords){
10596                 this.modified = [];
10597             }
10598             for(var i = 0, len = r.length; i < len; i++){
10599                 r[i].join(this);
10600             }
10601             if(this.snapshot){
10602                 this.data = this.snapshot;
10603                 delete this.snapshot;
10604             }
10605             this.data.clear();
10606             this.data.addAll(r);
10607             this.totalLength = t;
10608             this.applySort();
10609             this.fireEvent("datachanged", this);
10610         }else{
10611             this.totalLength = Math.max(t, this.data.length+r.length);
10612             this.add(r);
10613         }
10614         this.fireEvent("load", this, r, options, o);
10615         if(options.callback){
10616             options.callback.call(options.scope || this, r, options, true);
10617         }
10618     },
10619
10620
10621     /**
10622      * Loads data from a passed data block. A Reader which understands the format of the data
10623      * must have been configured in the constructor.
10624      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10625      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10626      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10627      */
10628     loadData : function(o, append){
10629         var r = this.reader.readRecords(o);
10630         this.loadRecords(r, {add: append}, true);
10631     },
10632
10633     /**
10634      * Gets the number of cached records.
10635      * <p>
10636      * <em>If using paging, this may not be the total size of the dataset. If the data object
10637      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10638      * the data set size</em>
10639      */
10640     getCount : function(){
10641         return this.data.length || 0;
10642     },
10643
10644     /**
10645      * Gets the total number of records in the dataset as returned by the server.
10646      * <p>
10647      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10648      * the dataset size</em>
10649      */
10650     getTotalCount : function(){
10651         return this.totalLength || 0;
10652     },
10653
10654     /**
10655      * Returns the sort state of the Store as an object with two properties:
10656      * <pre><code>
10657  field {String} The name of the field by which the Records are sorted
10658  direction {String} The sort order, "ASC" or "DESC"
10659      * </code></pre>
10660      */
10661     getSortState : function(){
10662         return this.sortInfo;
10663     },
10664
10665     // private
10666     applySort : function(){
10667         if(this.sortInfo && !this.remoteSort){
10668             var s = this.sortInfo, f = s.field;
10669             var st = this.fields.get(f).sortType;
10670             var fn = function(r1, r2){
10671                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10672                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10673             };
10674             this.data.sort(s.direction, fn);
10675             if(this.snapshot && this.snapshot != this.data){
10676                 this.snapshot.sort(s.direction, fn);
10677             }
10678         }
10679     },
10680
10681     /**
10682      * Sets the default sort column and order to be used by the next load operation.
10683      * @param {String} fieldName The name of the field to sort by.
10684      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10685      */
10686     setDefaultSort : function(field, dir){
10687         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10688     },
10689
10690     /**
10691      * Sort the Records.
10692      * If remote sorting is used, the sort is performed on the server, and the cache is
10693      * reloaded. If local sorting is used, the cache is sorted internally.
10694      * @param {String} fieldName The name of the field to sort by.
10695      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10696      */
10697     sort : function(fieldName, dir){
10698         var f = this.fields.get(fieldName);
10699         if(!dir){
10700             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10701             
10702             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10703                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10704             }else{
10705                 dir = f.sortDir;
10706             }
10707         }
10708         this.sortToggle[f.name] = dir;
10709         this.sortInfo = {field: f.name, direction: dir};
10710         if(!this.remoteSort){
10711             this.applySort();
10712             this.fireEvent("datachanged", this);
10713         }else{
10714             this.load(this.lastOptions);
10715         }
10716     },
10717
10718     /**
10719      * Calls the specified function for each of the Records in the cache.
10720      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10721      * Returning <em>false</em> aborts and exits the iteration.
10722      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10723      */
10724     each : function(fn, scope){
10725         this.data.each(fn, scope);
10726     },
10727
10728     /**
10729      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10730      * (e.g., during paging).
10731      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10732      */
10733     getModifiedRecords : function(){
10734         return this.modified;
10735     },
10736
10737     // private
10738     createFilterFn : function(property, value, anyMatch){
10739         if(!value.exec){ // not a regex
10740             value = String(value);
10741             if(value.length == 0){
10742                 return false;
10743             }
10744             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10745         }
10746         return function(r){
10747             return value.test(r.data[property]);
10748         };
10749     },
10750
10751     /**
10752      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10753      * @param {String} property A field on your records
10754      * @param {Number} start The record index to start at (defaults to 0)
10755      * @param {Number} end The last record index to include (defaults to length - 1)
10756      * @return {Number} The sum
10757      */
10758     sum : function(property, start, end){
10759         var rs = this.data.items, v = 0;
10760         start = start || 0;
10761         end = (end || end === 0) ? end : rs.length-1;
10762
10763         for(var i = start; i <= end; i++){
10764             v += (rs[i].data[property] || 0);
10765         }
10766         return v;
10767     },
10768
10769     /**
10770      * Filter the records by a specified property.
10771      * @param {String} field A field on your records
10772      * @param {String/RegExp} value Either a string that the field
10773      * should start with or a RegExp to test against the field
10774      * @param {Boolean} anyMatch True to match any part not just the beginning
10775      */
10776     filter : function(property, value, anyMatch){
10777         var fn = this.createFilterFn(property, value, anyMatch);
10778         return fn ? this.filterBy(fn) : this.clearFilter();
10779     },
10780
10781     /**
10782      * Filter by a function. The specified function will be called with each
10783      * record in this data source. If the function returns true the record is included,
10784      * otherwise it is filtered.
10785      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10786      * @param {Object} scope (optional) The scope of the function (defaults to this)
10787      */
10788     filterBy : function(fn, scope){
10789         this.snapshot = this.snapshot || this.data;
10790         this.data = this.queryBy(fn, scope||this);
10791         this.fireEvent("datachanged", this);
10792     },
10793
10794     /**
10795      * Query the records by a specified property.
10796      * @param {String} field A field on your records
10797      * @param {String/RegExp} value Either a string that the field
10798      * should start with or a RegExp to test against the field
10799      * @param {Boolean} anyMatch True to match any part not just the beginning
10800      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10801      */
10802     query : function(property, value, anyMatch){
10803         var fn = this.createFilterFn(property, value, anyMatch);
10804         return fn ? this.queryBy(fn) : this.data.clone();
10805     },
10806
10807     /**
10808      * Query by a function. The specified function will be called with each
10809      * record in this data source. If the function returns true the record is included
10810      * in the results.
10811      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10812      * @param {Object} scope (optional) The scope of the function (defaults to this)
10813       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10814      **/
10815     queryBy : function(fn, scope){
10816         var data = this.snapshot || this.data;
10817         return data.filterBy(fn, scope||this);
10818     },
10819
10820     /**
10821      * Collects unique values for a particular dataIndex from this store.
10822      * @param {String} dataIndex The property to collect
10823      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10824      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10825      * @return {Array} An array of the unique values
10826      **/
10827     collect : function(dataIndex, allowNull, bypassFilter){
10828         var d = (bypassFilter === true && this.snapshot) ?
10829                 this.snapshot.items : this.data.items;
10830         var v, sv, r = [], l = {};
10831         for(var i = 0, len = d.length; i < len; i++){
10832             v = d[i].data[dataIndex];
10833             sv = String(v);
10834             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10835                 l[sv] = true;
10836                 r[r.length] = v;
10837             }
10838         }
10839         return r;
10840     },
10841
10842     /**
10843      * Revert to a view of the Record cache with no filtering applied.
10844      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10845      */
10846     clearFilter : function(suppressEvent){
10847         if(this.snapshot && this.snapshot != this.data){
10848             this.data = this.snapshot;
10849             delete this.snapshot;
10850             if(suppressEvent !== true){
10851                 this.fireEvent("datachanged", this);
10852             }
10853         }
10854     },
10855
10856     // private
10857     afterEdit : function(record){
10858         if(this.modified.indexOf(record) == -1){
10859             this.modified.push(record);
10860         }
10861         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10862     },
10863     
10864     // private
10865     afterReject : function(record){
10866         this.modified.remove(record);
10867         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10868     },
10869
10870     // private
10871     afterCommit : function(record){
10872         this.modified.remove(record);
10873         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10874     },
10875
10876     /**
10877      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10878      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10879      */
10880     commitChanges : function(){
10881         var m = this.modified.slice(0);
10882         this.modified = [];
10883         for(var i = 0, len = m.length; i < len; i++){
10884             m[i].commit();
10885         }
10886     },
10887
10888     /**
10889      * Cancel outstanding changes on all changed records.
10890      */
10891     rejectChanges : function(){
10892         var m = this.modified.slice(0);
10893         this.modified = [];
10894         for(var i = 0, len = m.length; i < len; i++){
10895             m[i].reject();
10896         }
10897     },
10898
10899     onMetaChange : function(meta, rtype, o){
10900         this.recordType = rtype;
10901         this.fields = rtype.prototype.fields;
10902         delete this.snapshot;
10903         this.sortInfo = meta.sortInfo || this.sortInfo;
10904         this.modified = [];
10905         this.fireEvent('metachange', this, this.reader.meta);
10906     },
10907     
10908     moveIndex : function(data, type)
10909     {
10910         var index = this.indexOf(data);
10911         
10912         var newIndex = index + type;
10913         
10914         this.remove(data);
10915         
10916         this.insert(newIndex, data);
10917         
10918     }
10919 });/*
10920  * Based on:
10921  * Ext JS Library 1.1.1
10922  * Copyright(c) 2006-2007, Ext JS, LLC.
10923  *
10924  * Originally Released Under LGPL - original licence link has changed is not relivant.
10925  *
10926  * Fork - LGPL
10927  * <script type="text/javascript">
10928  */
10929
10930 /**
10931  * @class Roo.data.SimpleStore
10932  * @extends Roo.data.Store
10933  * Small helper class to make creating Stores from Array data easier.
10934  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10935  * @cfg {Array} fields An array of field definition objects, or field name strings.
10936  * @cfg {Array} data The multi-dimensional array of data
10937  * @constructor
10938  * @param {Object} config
10939  */
10940 Roo.data.SimpleStore = function(config){
10941     Roo.data.SimpleStore.superclass.constructor.call(this, {
10942         isLocal : true,
10943         reader: new Roo.data.ArrayReader({
10944                 id: config.id
10945             },
10946             Roo.data.Record.create(config.fields)
10947         ),
10948         proxy : new Roo.data.MemoryProxy(config.data)
10949     });
10950     this.load();
10951 };
10952 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10953  * Based on:
10954  * Ext JS Library 1.1.1
10955  * Copyright(c) 2006-2007, Ext JS, LLC.
10956  *
10957  * Originally Released Under LGPL - original licence link has changed is not relivant.
10958  *
10959  * Fork - LGPL
10960  * <script type="text/javascript">
10961  */
10962
10963 /**
10964 /**
10965  * @extends Roo.data.Store
10966  * @class Roo.data.JsonStore
10967  * Small helper class to make creating Stores for JSON data easier. <br/>
10968 <pre><code>
10969 var store = new Roo.data.JsonStore({
10970     url: 'get-images.php',
10971     root: 'images',
10972     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10973 });
10974 </code></pre>
10975  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10976  * JsonReader and HttpProxy (unless inline data is provided).</b>
10977  * @cfg {Array} fields An array of field definition objects, or field name strings.
10978  * @constructor
10979  * @param {Object} config
10980  */
10981 Roo.data.JsonStore = function(c){
10982     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10983         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10984         reader: new Roo.data.JsonReader(c, c.fields)
10985     }));
10986 };
10987 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10988  * Based on:
10989  * Ext JS Library 1.1.1
10990  * Copyright(c) 2006-2007, Ext JS, LLC.
10991  *
10992  * Originally Released Under LGPL - original licence link has changed is not relivant.
10993  *
10994  * Fork - LGPL
10995  * <script type="text/javascript">
10996  */
10997
10998  
10999 Roo.data.Field = function(config){
11000     if(typeof config == "string"){
11001         config = {name: config};
11002     }
11003     Roo.apply(this, config);
11004     
11005     if(!this.type){
11006         this.type = "auto";
11007     }
11008     
11009     var st = Roo.data.SortTypes;
11010     // named sortTypes are supported, here we look them up
11011     if(typeof this.sortType == "string"){
11012         this.sortType = st[this.sortType];
11013     }
11014     
11015     // set default sortType for strings and dates
11016     if(!this.sortType){
11017         switch(this.type){
11018             case "string":
11019                 this.sortType = st.asUCString;
11020                 break;
11021             case "date":
11022                 this.sortType = st.asDate;
11023                 break;
11024             default:
11025                 this.sortType = st.none;
11026         }
11027     }
11028
11029     // define once
11030     var stripRe = /[\$,%]/g;
11031
11032     // prebuilt conversion function for this field, instead of
11033     // switching every time we're reading a value
11034     if(!this.convert){
11035         var cv, dateFormat = this.dateFormat;
11036         switch(this.type){
11037             case "":
11038             case "auto":
11039             case undefined:
11040                 cv = function(v){ return v; };
11041                 break;
11042             case "string":
11043                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11044                 break;
11045             case "int":
11046                 cv = function(v){
11047                     return v !== undefined && v !== null && v !== '' ?
11048                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11049                     };
11050                 break;
11051             case "float":
11052                 cv = function(v){
11053                     return v !== undefined && v !== null && v !== '' ?
11054                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11055                     };
11056                 break;
11057             case "bool":
11058             case "boolean":
11059                 cv = function(v){ return v === true || v === "true" || v == 1; };
11060                 break;
11061             case "date":
11062                 cv = function(v){
11063                     if(!v){
11064                         return '';
11065                     }
11066                     if(v instanceof Date){
11067                         return v;
11068                     }
11069                     if(dateFormat){
11070                         if(dateFormat == "timestamp"){
11071                             return new Date(v*1000);
11072                         }
11073                         return Date.parseDate(v, dateFormat);
11074                     }
11075                     var parsed = Date.parse(v);
11076                     return parsed ? new Date(parsed) : null;
11077                 };
11078              break;
11079             
11080         }
11081         this.convert = cv;
11082     }
11083 };
11084
11085 Roo.data.Field.prototype = {
11086     dateFormat: null,
11087     defaultValue: "",
11088     mapping: null,
11089     sortType : null,
11090     sortDir : "ASC"
11091 };/*
11092  * Based on:
11093  * Ext JS Library 1.1.1
11094  * Copyright(c) 2006-2007, Ext JS, LLC.
11095  *
11096  * Originally Released Under LGPL - original licence link has changed is not relivant.
11097  *
11098  * Fork - LGPL
11099  * <script type="text/javascript">
11100  */
11101  
11102 // Base class for reading structured data from a data source.  This class is intended to be
11103 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11104
11105 /**
11106  * @class Roo.data.DataReader
11107  * Base class for reading structured data from a data source.  This class is intended to be
11108  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11109  */
11110
11111 Roo.data.DataReader = function(meta, recordType){
11112     
11113     this.meta = meta;
11114     
11115     this.recordType = recordType instanceof Array ? 
11116         Roo.data.Record.create(recordType) : recordType;
11117 };
11118
11119 Roo.data.DataReader.prototype = {
11120      /**
11121      * Create an empty record
11122      * @param {Object} data (optional) - overlay some values
11123      * @return {Roo.data.Record} record created.
11124      */
11125     newRow :  function(d) {
11126         var da =  {};
11127         this.recordType.prototype.fields.each(function(c) {
11128             switch( c.type) {
11129                 case 'int' : da[c.name] = 0; break;
11130                 case 'date' : da[c.name] = new Date(); break;
11131                 case 'float' : da[c.name] = 0.0; break;
11132                 case 'boolean' : da[c.name] = false; break;
11133                 default : da[c.name] = ""; break;
11134             }
11135             
11136         });
11137         return new this.recordType(Roo.apply(da, d));
11138     }
11139     
11140 };/*
11141  * Based on:
11142  * Ext JS Library 1.1.1
11143  * Copyright(c) 2006-2007, Ext JS, LLC.
11144  *
11145  * Originally Released Under LGPL - original licence link has changed is not relivant.
11146  *
11147  * Fork - LGPL
11148  * <script type="text/javascript">
11149  */
11150
11151 /**
11152  * @class Roo.data.DataProxy
11153  * @extends Roo.data.Observable
11154  * This class is an abstract base class for implementations which provide retrieval of
11155  * unformatted data objects.<br>
11156  * <p>
11157  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11158  * (of the appropriate type which knows how to parse the data object) to provide a block of
11159  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11160  * <p>
11161  * Custom implementations must implement the load method as described in
11162  * {@link Roo.data.HttpProxy#load}.
11163  */
11164 Roo.data.DataProxy = function(){
11165     this.addEvents({
11166         /**
11167          * @event beforeload
11168          * Fires before a network request is made to retrieve a data object.
11169          * @param {Object} This DataProxy object.
11170          * @param {Object} params The params parameter to the load function.
11171          */
11172         beforeload : true,
11173         /**
11174          * @event load
11175          * Fires before the load method's callback is called.
11176          * @param {Object} This DataProxy object.
11177          * @param {Object} o The data object.
11178          * @param {Object} arg The callback argument object passed to the load function.
11179          */
11180         load : true,
11181         /**
11182          * @event loadexception
11183          * Fires if an Exception occurs during data retrieval.
11184          * @param {Object} This DataProxy object.
11185          * @param {Object} o The data object.
11186          * @param {Object} arg The callback argument object passed to the load function.
11187          * @param {Object} e The Exception.
11188          */
11189         loadexception : true
11190     });
11191     Roo.data.DataProxy.superclass.constructor.call(this);
11192 };
11193
11194 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11195
11196     /**
11197      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11198      */
11199 /*
11200  * Based on:
11201  * Ext JS Library 1.1.1
11202  * Copyright(c) 2006-2007, Ext JS, LLC.
11203  *
11204  * Originally Released Under LGPL - original licence link has changed is not relivant.
11205  *
11206  * Fork - LGPL
11207  * <script type="text/javascript">
11208  */
11209 /**
11210  * @class Roo.data.MemoryProxy
11211  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11212  * to the Reader when its load method is called.
11213  * @constructor
11214  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11215  */
11216 Roo.data.MemoryProxy = function(data){
11217     if (data.data) {
11218         data = data.data;
11219     }
11220     Roo.data.MemoryProxy.superclass.constructor.call(this);
11221     this.data = data;
11222 };
11223
11224 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11225     
11226     /**
11227      * Load data from the requested source (in this case an in-memory
11228      * data object passed to the constructor), read the data object into
11229      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11230      * process that block using the passed callback.
11231      * @param {Object} params This parameter is not used by the MemoryProxy class.
11232      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11233      * object into a block of Roo.data.Records.
11234      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11235      * The function must be passed <ul>
11236      * <li>The Record block object</li>
11237      * <li>The "arg" argument from the load function</li>
11238      * <li>A boolean success indicator</li>
11239      * </ul>
11240      * @param {Object} scope The scope in which to call the callback
11241      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11242      */
11243     load : function(params, reader, callback, scope, arg){
11244         params = params || {};
11245         var result;
11246         try {
11247             result = reader.readRecords(this.data);
11248         }catch(e){
11249             this.fireEvent("loadexception", this, arg, null, e);
11250             callback.call(scope, null, arg, false);
11251             return;
11252         }
11253         callback.call(scope, result, arg, true);
11254     },
11255     
11256     // private
11257     update : function(params, records){
11258         
11259     }
11260 });/*
11261  * Based on:
11262  * Ext JS Library 1.1.1
11263  * Copyright(c) 2006-2007, Ext JS, LLC.
11264  *
11265  * Originally Released Under LGPL - original licence link has changed is not relivant.
11266  *
11267  * Fork - LGPL
11268  * <script type="text/javascript">
11269  */
11270 /**
11271  * @class Roo.data.HttpProxy
11272  * @extends Roo.data.DataProxy
11273  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11274  * configured to reference a certain URL.<br><br>
11275  * <p>
11276  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11277  * from which the running page was served.<br><br>
11278  * <p>
11279  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11280  * <p>
11281  * Be aware that to enable the browser to parse an XML document, the server must set
11282  * the Content-Type header in the HTTP response to "text/xml".
11283  * @constructor
11284  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11285  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11286  * will be used to make the request.
11287  */
11288 Roo.data.HttpProxy = function(conn){
11289     Roo.data.HttpProxy.superclass.constructor.call(this);
11290     // is conn a conn config or a real conn?
11291     this.conn = conn;
11292     this.useAjax = !conn || !conn.events;
11293   
11294 };
11295
11296 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11297     // thse are take from connection...
11298     
11299     /**
11300      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11301      */
11302     /**
11303      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11304      * extra parameters to each request made by this object. (defaults to undefined)
11305      */
11306     /**
11307      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11308      *  to each request made by this object. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11312      */
11313     /**
11314      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11315      */
11316      /**
11317      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11318      * @type Boolean
11319      */
11320   
11321
11322     /**
11323      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11324      * @type Boolean
11325      */
11326     /**
11327      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11328      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11329      * a finer-grained basis than the DataProxy events.
11330      */
11331     getConnection : function(){
11332         return this.useAjax ? Roo.Ajax : this.conn;
11333     },
11334
11335     /**
11336      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11337      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11338      * process that block using the passed callback.
11339      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11340      * for the request to the remote server.
11341      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11342      * object into a block of Roo.data.Records.
11343      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11344      * The function must be passed <ul>
11345      * <li>The Record block object</li>
11346      * <li>The "arg" argument from the load function</li>
11347      * <li>A boolean success indicator</li>
11348      * </ul>
11349      * @param {Object} scope The scope in which to call the callback
11350      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11351      */
11352     load : function(params, reader, callback, scope, arg){
11353         if(this.fireEvent("beforeload", this, params) !== false){
11354             var  o = {
11355                 params : params || {},
11356                 request: {
11357                     callback : callback,
11358                     scope : scope,
11359                     arg : arg
11360                 },
11361                 reader: reader,
11362                 callback : this.loadResponse,
11363                 scope: this
11364             };
11365             if(this.useAjax){
11366                 Roo.applyIf(o, this.conn);
11367                 if(this.activeRequest){
11368                     Roo.Ajax.abort(this.activeRequest);
11369                 }
11370                 this.activeRequest = Roo.Ajax.request(o);
11371             }else{
11372                 this.conn.request(o);
11373             }
11374         }else{
11375             callback.call(scope||this, null, arg, false);
11376         }
11377     },
11378
11379     // private
11380     loadResponse : function(o, success, response){
11381         delete this.activeRequest;
11382         if(!success){
11383             this.fireEvent("loadexception", this, o, response);
11384             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11385             return;
11386         }
11387         var result;
11388         try {
11389             result = o.reader.read(response);
11390         }catch(e){
11391             this.fireEvent("loadexception", this, o, response, e);
11392             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11393             return;
11394         }
11395         
11396         this.fireEvent("load", this, o, o.request.arg);
11397         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11398     },
11399
11400     // private
11401     update : function(dataSet){
11402
11403     },
11404
11405     // private
11406     updateResponse : function(dataSet){
11407
11408     }
11409 });/*
11410  * Based on:
11411  * Ext JS Library 1.1.1
11412  * Copyright(c) 2006-2007, Ext JS, LLC.
11413  *
11414  * Originally Released Under LGPL - original licence link has changed is not relivant.
11415  *
11416  * Fork - LGPL
11417  * <script type="text/javascript">
11418  */
11419
11420 /**
11421  * @class Roo.data.ScriptTagProxy
11422  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11423  * other than the originating domain of the running page.<br><br>
11424  * <p>
11425  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11426  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11427  * <p>
11428  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11429  * source code that is used as the source inside a &lt;script> tag.<br><br>
11430  * <p>
11431  * In order for the browser to process the returned data, the server must wrap the data object
11432  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11433  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11434  * depending on whether the callback name was passed:
11435  * <p>
11436  * <pre><code>
11437 boolean scriptTag = false;
11438 String cb = request.getParameter("callback");
11439 if (cb != null) {
11440     scriptTag = true;
11441     response.setContentType("text/javascript");
11442 } else {
11443     response.setContentType("application/x-json");
11444 }
11445 Writer out = response.getWriter();
11446 if (scriptTag) {
11447     out.write(cb + "(");
11448 }
11449 out.print(dataBlock.toJsonString());
11450 if (scriptTag) {
11451     out.write(");");
11452 }
11453 </pre></code>
11454  *
11455  * @constructor
11456  * @param {Object} config A configuration object.
11457  */
11458 Roo.data.ScriptTagProxy = function(config){
11459     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11460     Roo.apply(this, config);
11461     this.head = document.getElementsByTagName("head")[0];
11462 };
11463
11464 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11465
11466 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11467     /**
11468      * @cfg {String} url The URL from which to request the data object.
11469      */
11470     /**
11471      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11472      */
11473     timeout : 30000,
11474     /**
11475      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11476      * the server the name of the callback function set up by the load call to process the returned data object.
11477      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11478      * javascript output which calls this named function passing the data object as its only parameter.
11479      */
11480     callbackParam : "callback",
11481     /**
11482      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11483      * name to the request.
11484      */
11485     nocache : true,
11486
11487     /**
11488      * Load data from the configured URL, read the data object into
11489      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11490      * process that block using the passed callback.
11491      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11492      * for the request to the remote server.
11493      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11494      * object into a block of Roo.data.Records.
11495      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11496      * The function must be passed <ul>
11497      * <li>The Record block object</li>
11498      * <li>The "arg" argument from the load function</li>
11499      * <li>A boolean success indicator</li>
11500      * </ul>
11501      * @param {Object} scope The scope in which to call the callback
11502      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11503      */
11504     load : function(params, reader, callback, scope, arg){
11505         if(this.fireEvent("beforeload", this, params) !== false){
11506
11507             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11508
11509             var url = this.url;
11510             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11511             if(this.nocache){
11512                 url += "&_dc=" + (new Date().getTime());
11513             }
11514             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11515             var trans = {
11516                 id : transId,
11517                 cb : "stcCallback"+transId,
11518                 scriptId : "stcScript"+transId,
11519                 params : params,
11520                 arg : arg,
11521                 url : url,
11522                 callback : callback,
11523                 scope : scope,
11524                 reader : reader
11525             };
11526             var conn = this;
11527
11528             window[trans.cb] = function(o){
11529                 conn.handleResponse(o, trans);
11530             };
11531
11532             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11533
11534             if(this.autoAbort !== false){
11535                 this.abort();
11536             }
11537
11538             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11539
11540             var script = document.createElement("script");
11541             script.setAttribute("src", url);
11542             script.setAttribute("type", "text/javascript");
11543             script.setAttribute("id", trans.scriptId);
11544             this.head.appendChild(script);
11545
11546             this.trans = trans;
11547         }else{
11548             callback.call(scope||this, null, arg, false);
11549         }
11550     },
11551
11552     // private
11553     isLoading : function(){
11554         return this.trans ? true : false;
11555     },
11556
11557     /**
11558      * Abort the current server request.
11559      */
11560     abort : function(){
11561         if(this.isLoading()){
11562             this.destroyTrans(this.trans);
11563         }
11564     },
11565
11566     // private
11567     destroyTrans : function(trans, isLoaded){
11568         this.head.removeChild(document.getElementById(trans.scriptId));
11569         clearTimeout(trans.timeoutId);
11570         if(isLoaded){
11571             window[trans.cb] = undefined;
11572             try{
11573                 delete window[trans.cb];
11574             }catch(e){}
11575         }else{
11576             // if hasn't been loaded, wait for load to remove it to prevent script error
11577             window[trans.cb] = function(){
11578                 window[trans.cb] = undefined;
11579                 try{
11580                     delete window[trans.cb];
11581                 }catch(e){}
11582             };
11583         }
11584     },
11585
11586     // private
11587     handleResponse : function(o, trans){
11588         this.trans = false;
11589         this.destroyTrans(trans, true);
11590         var result;
11591         try {
11592             result = trans.reader.readRecords(o);
11593         }catch(e){
11594             this.fireEvent("loadexception", this, o, trans.arg, e);
11595             trans.callback.call(trans.scope||window, null, trans.arg, false);
11596             return;
11597         }
11598         this.fireEvent("load", this, o, trans.arg);
11599         trans.callback.call(trans.scope||window, result, trans.arg, true);
11600     },
11601
11602     // private
11603     handleFailure : function(trans){
11604         this.trans = false;
11605         this.destroyTrans(trans, false);
11606         this.fireEvent("loadexception", this, null, trans.arg);
11607         trans.callback.call(trans.scope||window, null, trans.arg, false);
11608     }
11609 });/*
11610  * Based on:
11611  * Ext JS Library 1.1.1
11612  * Copyright(c) 2006-2007, Ext JS, LLC.
11613  *
11614  * Originally Released Under LGPL - original licence link has changed is not relivant.
11615  *
11616  * Fork - LGPL
11617  * <script type="text/javascript">
11618  */
11619
11620 /**
11621  * @class Roo.data.JsonReader
11622  * @extends Roo.data.DataReader
11623  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11624  * based on mappings in a provided Roo.data.Record constructor.
11625  * 
11626  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11627  * in the reply previously. 
11628  * 
11629  * <p>
11630  * Example code:
11631  * <pre><code>
11632 var RecordDef = Roo.data.Record.create([
11633     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11634     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11635 ]);
11636 var myReader = new Roo.data.JsonReader({
11637     totalProperty: "results",    // The property which contains the total dataset size (optional)
11638     root: "rows",                // The property which contains an Array of row objects
11639     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11640 }, RecordDef);
11641 </code></pre>
11642  * <p>
11643  * This would consume a JSON file like this:
11644  * <pre><code>
11645 { 'results': 2, 'rows': [
11646     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11647     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11648 }
11649 </code></pre>
11650  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11651  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11652  * paged from the remote server.
11653  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11654  * @cfg {String} root name of the property which contains the Array of row objects.
11655  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11656  * @cfg {Array} fields Array of field definition objects
11657  * @constructor
11658  * Create a new JsonReader
11659  * @param {Object} meta Metadata configuration options
11660  * @param {Object} recordType Either an Array of field definition objects,
11661  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11662  */
11663 Roo.data.JsonReader = function(meta, recordType){
11664     
11665     meta = meta || {};
11666     // set some defaults:
11667     Roo.applyIf(meta, {
11668         totalProperty: 'total',
11669         successProperty : 'success',
11670         root : 'data',
11671         id : 'id'
11672     });
11673     
11674     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11675 };
11676 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11677     
11678     /**
11679      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11680      * Used by Store query builder to append _requestMeta to params.
11681      * 
11682      */
11683     metaFromRemote : false,
11684     /**
11685      * This method is only used by a DataProxy which has retrieved data from a remote server.
11686      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11687      * @return {Object} data A data block which is used by an Roo.data.Store object as
11688      * a cache of Roo.data.Records.
11689      */
11690     read : function(response){
11691         var json = response.responseText;
11692        
11693         var o = /* eval:var:o */ eval("("+json+")");
11694         if(!o) {
11695             throw {message: "JsonReader.read: Json object not found"};
11696         }
11697         
11698         if(o.metaData){
11699             
11700             delete this.ef;
11701             this.metaFromRemote = true;
11702             this.meta = o.metaData;
11703             this.recordType = Roo.data.Record.create(o.metaData.fields);
11704             this.onMetaChange(this.meta, this.recordType, o);
11705         }
11706         return this.readRecords(o);
11707     },
11708
11709     // private function a store will implement
11710     onMetaChange : function(meta, recordType, o){
11711
11712     },
11713
11714     /**
11715          * @ignore
11716          */
11717     simpleAccess: function(obj, subsc) {
11718         return obj[subsc];
11719     },
11720
11721         /**
11722          * @ignore
11723          */
11724     getJsonAccessor: function(){
11725         var re = /[\[\.]/;
11726         return function(expr) {
11727             try {
11728                 return(re.test(expr))
11729                     ? new Function("obj", "return obj." + expr)
11730                     : function(obj){
11731                         return obj[expr];
11732                     };
11733             } catch(e){}
11734             return Roo.emptyFn;
11735         };
11736     }(),
11737
11738     /**
11739      * Create a data block containing Roo.data.Records from an XML document.
11740      * @param {Object} o An object which contains an Array of row objects in the property specified
11741      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11742      * which contains the total size of the dataset.
11743      * @return {Object} data A data block which is used by an Roo.data.Store object as
11744      * a cache of Roo.data.Records.
11745      */
11746     readRecords : function(o){
11747         /**
11748          * After any data loads, the raw JSON data is available for further custom processing.
11749          * @type Object
11750          */
11751         this.o = o;
11752         var s = this.meta, Record = this.recordType,
11753             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11754
11755 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11756         if (!this.ef) {
11757             if(s.totalProperty) {
11758                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11759                 }
11760                 if(s.successProperty) {
11761                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11762                 }
11763                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11764                 if (s.id) {
11765                         var g = this.getJsonAccessor(s.id);
11766                         this.getId = function(rec) {
11767                                 var r = g(rec);  
11768                                 return (r === undefined || r === "") ? null : r;
11769                         };
11770                 } else {
11771                         this.getId = function(){return null;};
11772                 }
11773             this.ef = [];
11774             for(var jj = 0; jj < fl; jj++){
11775                 f = fi[jj];
11776                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11777                 this.ef[jj] = this.getJsonAccessor(map);
11778             }
11779         }
11780
11781         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11782         if(s.totalProperty){
11783             var vt = parseInt(this.getTotal(o), 10);
11784             if(!isNaN(vt)){
11785                 totalRecords = vt;
11786             }
11787         }
11788         if(s.successProperty){
11789             var vs = this.getSuccess(o);
11790             if(vs === false || vs === 'false'){
11791                 success = false;
11792             }
11793         }
11794         var records = [];
11795         for(var i = 0; i < c; i++){
11796                 var n = root[i];
11797             var values = {};
11798             var id = this.getId(n);
11799             for(var j = 0; j < fl; j++){
11800                 f = fi[j];
11801             var v = this.ef[j](n);
11802             if (!f.convert) {
11803                 Roo.log('missing convert for ' + f.name);
11804                 Roo.log(f);
11805                 continue;
11806             }
11807             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11808             }
11809             var record = new Record(values, id);
11810             record.json = n;
11811             records[i] = record;
11812         }
11813         return {
11814             raw : o,
11815             success : success,
11816             records : records,
11817             totalRecords : totalRecords
11818         };
11819     }
11820 });/*
11821  * Based on:
11822  * Ext JS Library 1.1.1
11823  * Copyright(c) 2006-2007, Ext JS, LLC.
11824  *
11825  * Originally Released Under LGPL - original licence link has changed is not relivant.
11826  *
11827  * Fork - LGPL
11828  * <script type="text/javascript">
11829  */
11830
11831 /**
11832  * @class Roo.data.ArrayReader
11833  * @extends Roo.data.DataReader
11834  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11835  * Each element of that Array represents a row of data fields. The
11836  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11837  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11838  * <p>
11839  * Example code:.
11840  * <pre><code>
11841 var RecordDef = Roo.data.Record.create([
11842     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11843     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11844 ]);
11845 var myReader = new Roo.data.ArrayReader({
11846     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11847 }, RecordDef);
11848 </code></pre>
11849  * <p>
11850  * This would consume an Array like this:
11851  * <pre><code>
11852 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11853   </code></pre>
11854  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11855  * @constructor
11856  * Create a new JsonReader
11857  * @param {Object} meta Metadata configuration options.
11858  * @param {Object} recordType Either an Array of field definition objects
11859  * as specified to {@link Roo.data.Record#create},
11860  * or an {@link Roo.data.Record} object
11861  * created using {@link Roo.data.Record#create}.
11862  */
11863 Roo.data.ArrayReader = function(meta, recordType){
11864     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11865 };
11866
11867 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11868     /**
11869      * Create a data block containing Roo.data.Records from an XML document.
11870      * @param {Object} o An Array of row objects which represents the dataset.
11871      * @return {Object} data A data block which is used by an Roo.data.Store object as
11872      * a cache of Roo.data.Records.
11873      */
11874     readRecords : function(o){
11875         var sid = this.meta ? this.meta.id : null;
11876         var recordType = this.recordType, fields = recordType.prototype.fields;
11877         var records = [];
11878         var root = o;
11879             for(var i = 0; i < root.length; i++){
11880                     var n = root[i];
11881                 var values = {};
11882                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11883                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11884                 var f = fields.items[j];
11885                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11886                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11887                 v = f.convert(v);
11888                 values[f.name] = v;
11889             }
11890                 var record = new recordType(values, id);
11891                 record.json = n;
11892                 records[records.length] = record;
11893             }
11894             return {
11895                 records : records,
11896                 totalRecords : records.length
11897             };
11898     }
11899 });/*
11900  * - LGPL
11901  * * 
11902  */
11903
11904 /**
11905  * @class Roo.bootstrap.ComboBox
11906  * @extends Roo.bootstrap.TriggerField
11907  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11908  * @cfg {Boolean} append (true|false) default false
11909  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11910  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11911  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11912  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11913  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11914  * @cfg {Boolean} animate default true
11915  * @cfg {Boolean} emptyResultText only for touch device
11916  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11917  * @constructor
11918  * Create a new ComboBox.
11919  * @param {Object} config Configuration options
11920  */
11921 Roo.bootstrap.ComboBox = function(config){
11922     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11923     this.addEvents({
11924         /**
11925          * @event expand
11926          * Fires when the dropdown list is expanded
11927              * @param {Roo.bootstrap.ComboBox} combo This combo box
11928              */
11929         'expand' : true,
11930         /**
11931          * @event collapse
11932          * Fires when the dropdown list is collapsed
11933              * @param {Roo.bootstrap.ComboBox} combo This combo box
11934              */
11935         'collapse' : true,
11936         /**
11937          * @event beforeselect
11938          * Fires before a list item is selected. Return false to cancel the selection.
11939              * @param {Roo.bootstrap.ComboBox} combo This combo box
11940              * @param {Roo.data.Record} record The data record returned from the underlying store
11941              * @param {Number} index The index of the selected item in the dropdown list
11942              */
11943         'beforeselect' : true,
11944         /**
11945          * @event select
11946          * Fires when a list item is selected
11947              * @param {Roo.bootstrap.ComboBox} combo This combo box
11948              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11949              * @param {Number} index The index of the selected item in the dropdown list
11950              */
11951         'select' : true,
11952         /**
11953          * @event beforequery
11954          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11955          * The event object passed has these properties:
11956              * @param {Roo.bootstrap.ComboBox} combo This combo box
11957              * @param {String} query The query
11958              * @param {Boolean} forceAll true to force "all" query
11959              * @param {Boolean} cancel true to cancel the query
11960              * @param {Object} e The query event object
11961              */
11962         'beforequery': true,
11963          /**
11964          * @event add
11965          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11966              * @param {Roo.bootstrap.ComboBox} combo This combo box
11967              */
11968         'add' : true,
11969         /**
11970          * @event edit
11971          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11972              * @param {Roo.bootstrap.ComboBox} combo This combo box
11973              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11974              */
11975         'edit' : true,
11976         /**
11977          * @event remove
11978          * Fires when the remove value from the combobox array
11979              * @param {Roo.bootstrap.ComboBox} combo This combo box
11980              */
11981         'remove' : true,
11982         /**
11983          * @event afterremove
11984          * Fires when the remove value from the combobox array
11985              * @param {Roo.bootstrap.ComboBox} combo This combo box
11986              */
11987         'afterremove' : true,
11988         /**
11989          * @event specialfilter
11990          * Fires when specialfilter
11991             * @param {Roo.bootstrap.ComboBox} combo This combo box
11992             */
11993         'specialfilter' : true,
11994         /**
11995          * @event tick
11996          * Fires when tick the element
11997             * @param {Roo.bootstrap.ComboBox} combo This combo box
11998             */
11999         'tick' : true,
12000         /**
12001          * @event touchviewdisplay
12002          * Fires when touch view require special display (default is using displayField)
12003             * @param {Roo.bootstrap.ComboBox} combo This combo box
12004             * @param {Object} cfg set html .
12005             */
12006         'touchviewdisplay' : true
12007         
12008     });
12009     
12010     this.item = [];
12011     this.tickItems = [];
12012     
12013     this.selectedIndex = -1;
12014     if(this.mode == 'local'){
12015         if(config.queryDelay === undefined){
12016             this.queryDelay = 10;
12017         }
12018         if(config.minChars === undefined){
12019             this.minChars = 0;
12020         }
12021     }
12022 };
12023
12024 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12025      
12026     /**
12027      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12028      * rendering into an Roo.Editor, defaults to false)
12029      */
12030     /**
12031      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12032      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12033      */
12034     /**
12035      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12036      */
12037     /**
12038      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12039      * the dropdown list (defaults to undefined, with no header element)
12040      */
12041
12042      /**
12043      * @cfg {String/Roo.Template} tpl The template to use to render the output
12044      */
12045      
12046      /**
12047      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12048      */
12049     listWidth: undefined,
12050     /**
12051      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12052      * mode = 'remote' or 'text' if mode = 'local')
12053      */
12054     displayField: undefined,
12055     
12056     /**
12057      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12058      * mode = 'remote' or 'value' if mode = 'local'). 
12059      * Note: use of a valueField requires the user make a selection
12060      * in order for a value to be mapped.
12061      */
12062     valueField: undefined,
12063     /**
12064      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12065      */
12066     modalTitle : '',
12067     
12068     /**
12069      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12070      * field's data value (defaults to the underlying DOM element's name)
12071      */
12072     hiddenName: undefined,
12073     /**
12074      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12075      */
12076     listClass: '',
12077     /**
12078      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12079      */
12080     selectedClass: 'active',
12081     
12082     /**
12083      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12084      */
12085     shadow:'sides',
12086     /**
12087      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12088      * anchor positions (defaults to 'tl-bl')
12089      */
12090     listAlign: 'tl-bl?',
12091     /**
12092      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12093      */
12094     maxHeight: 300,
12095     /**
12096      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12097      * query specified by the allQuery config option (defaults to 'query')
12098      */
12099     triggerAction: 'query',
12100     /**
12101      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12102      * (defaults to 4, does not apply if editable = false)
12103      */
12104     minChars : 4,
12105     /**
12106      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12107      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12108      */
12109     typeAhead: false,
12110     /**
12111      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12112      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12113      */
12114     queryDelay: 500,
12115     /**
12116      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12117      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12118      */
12119     pageSize: 0,
12120     /**
12121      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12122      * when editable = true (defaults to false)
12123      */
12124     selectOnFocus:false,
12125     /**
12126      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12127      */
12128     queryParam: 'query',
12129     /**
12130      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12131      * when mode = 'remote' (defaults to 'Loading...')
12132      */
12133     loadingText: 'Loading...',
12134     /**
12135      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12136      */
12137     resizable: false,
12138     /**
12139      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12140      */
12141     handleHeight : 8,
12142     /**
12143      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12144      * traditional select (defaults to true)
12145      */
12146     editable: true,
12147     /**
12148      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12149      */
12150     allQuery: '',
12151     /**
12152      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12153      */
12154     mode: 'remote',
12155     /**
12156      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12157      * listWidth has a higher value)
12158      */
12159     minListWidth : 70,
12160     /**
12161      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12162      * allow the user to set arbitrary text into the field (defaults to false)
12163      */
12164     forceSelection:false,
12165     /**
12166      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12167      * if typeAhead = true (defaults to 250)
12168      */
12169     typeAheadDelay : 250,
12170     /**
12171      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12172      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12173      */
12174     valueNotFoundText : undefined,
12175     /**
12176      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12177      */
12178     blockFocus : false,
12179     
12180     /**
12181      * @cfg {Boolean} disableClear Disable showing of clear button.
12182      */
12183     disableClear : false,
12184     /**
12185      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12186      */
12187     alwaysQuery : false,
12188     
12189     /**
12190      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12191      */
12192     multiple : false,
12193     
12194     /**
12195      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12196      */
12197     invalidClass : "has-warning",
12198     
12199     /**
12200      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12201      */
12202     validClass : "has-success",
12203     
12204     /**
12205      * @cfg {Boolean} specialFilter (true|false) special filter default false
12206      */
12207     specialFilter : false,
12208     
12209     /**
12210      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12211      */
12212     mobileTouchView : true,
12213     
12214     //private
12215     addicon : false,
12216     editicon: false,
12217     
12218     page: 0,
12219     hasQuery: false,
12220     append: false,
12221     loadNext: false,
12222     autoFocus : true,
12223     tickable : false,
12224     btnPosition : 'right',
12225     triggerList : true,
12226     showToggleBtn : true,
12227     animate : true,
12228     emptyResultText: 'Empty',
12229     triggerText : 'Select',
12230     
12231     // element that contains real text value.. (when hidden is used..)
12232     
12233     getAutoCreate : function()
12234     {
12235         var cfg = false;
12236         
12237         /*
12238          * Touch Devices
12239          */
12240         
12241         if(Roo.isTouch && this.mobileTouchView){
12242             cfg = this.getAutoCreateTouchView();
12243             return cfg;;
12244         }
12245         
12246         /*
12247          *  Normal ComboBox
12248          */
12249         if(!this.tickable){
12250             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12251             return cfg;
12252         }
12253         
12254         /*
12255          *  ComboBox with tickable selections
12256          */
12257              
12258         var align = this.labelAlign || this.parentLabelAlign();
12259         
12260         cfg = {
12261             cls : 'form-group roo-combobox-tickable' //input-group
12262         };
12263         
12264         var buttons = {
12265             tag : 'div',
12266             cls : 'tickable-buttons',
12267             cn : [
12268                 {
12269                     tag : 'button',
12270                     type : 'button',
12271                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12272                     html : this.triggerText
12273                 },
12274                 {
12275                     tag : 'button',
12276                     type : 'button',
12277                     name : 'ok',
12278                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12279                     html : 'Done'
12280                 },
12281                 {
12282                     tag : 'button',
12283                     type : 'button',
12284                     name : 'cancel',
12285                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12286                     html : 'Cancel'
12287                 }
12288             ]
12289         };
12290         
12291         if(this.editable){
12292             buttons.cn.unshift({
12293                 tag: 'input',
12294                 cls: 'roo-select2-search-field-input'
12295             });
12296         }
12297         
12298         var _this = this;
12299         
12300         Roo.each(buttons.cn, function(c){
12301             if (_this.size) {
12302                 c.cls += ' btn-' + _this.size;
12303             }
12304
12305             if (_this.disabled) {
12306                 c.disabled = true;
12307             }
12308         });
12309         
12310         var box = {
12311             tag: 'div',
12312             cn: [
12313                 {
12314                     tag: 'input',
12315                     type : 'hidden',
12316                     cls: 'form-hidden-field'
12317                 },
12318                 {
12319                     tag: 'ul',
12320                     cls: 'roo-select2-choices',
12321                     cn:[
12322                         {
12323                             tag: 'li',
12324                             cls: 'roo-select2-search-field',
12325                             cn: [
12326
12327                                 buttons
12328                             ]
12329                         }
12330                     ]
12331                 }
12332             ]
12333         };
12334         
12335         var combobox = {
12336             cls: 'roo-select2-container input-group roo-select2-container-multi',
12337             cn: [
12338                 box
12339 //                {
12340 //                    tag: 'ul',
12341 //                    cls: 'typeahead typeahead-long dropdown-menu',
12342 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12343 //                }
12344             ]
12345         };
12346         
12347         if(this.hasFeedback && !this.allowBlank){
12348             
12349             var feedback = {
12350                 tag: 'span',
12351                 cls: 'glyphicon form-control-feedback'
12352             };
12353
12354             combobox.cn.push(feedback);
12355         }
12356         
12357         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12358             
12359 //                Roo.log("left and has label");
12360             cfg.cn = [
12361                 {
12362                     tag : 'i',
12363                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12364                     tooltip : 'This field is required'
12365                 },
12366                 {
12367                     tag: 'label',
12368                     'for' :  id,
12369                     cls : 'control-label col-sm-' + this.labelWidth,
12370                     html : this.fieldLabel
12371
12372                 },
12373                 {
12374                     cls : "col-sm-" + (12 - this.labelWidth), 
12375                     cn: [
12376                         combobox
12377                     ]
12378                 }
12379
12380             ];
12381
12382             if(this.indicatorpos == 'right'){
12383                 
12384                 cfg.cn = [
12385                     {
12386                         tag: 'label',
12387                         'for' :  id,
12388                         cls : 'control-label col-sm-' + this.labelWidth,
12389                         html : this.fieldLabel
12390
12391                     },
12392                     {
12393                         tag : 'i',
12394                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12395                         tooltip : 'This field is required'
12396                     },
12397                     {
12398                         cls : "col-sm-" + (12 - this.labelWidth), 
12399                         cn: [
12400                             combobox
12401                         ]
12402                     }
12403
12404                 ];
12405             
12406             }
12407                 
12408                 
12409         } else if ( this.fieldLabel.length) {
12410 //                Roo.log(" label");
12411                  cfg.cn = [
12412                     {
12413                         tag : 'i',
12414                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12415                         tooltip : 'This field is required'
12416                     },
12417                     {
12418                         tag: 'label',
12419                         //cls : 'input-group-addon',
12420                         html : this.fieldLabel
12421                         
12422                     },
12423                     
12424                     combobox
12425                     
12426                 ];
12427                 
12428                 if(this.indicatorpos == 'right'){
12429                     
12430                     cfg.cn = [
12431                         {
12432                             tag: 'label',
12433                             //cls : 'input-group-addon',
12434                             html : this.fieldLabel
12435
12436                         },
12437                         
12438                         {
12439                             tag : 'i',
12440                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12441                             tooltip : 'This field is required'
12442                         },
12443                         
12444                         combobox
12445
12446                     ];
12447                 
12448                 }
12449
12450         } else {
12451             
12452 //                Roo.log(" no label && no align");
12453                 cfg = combobox
12454                      
12455                 
12456         }
12457          
12458         var settings=this;
12459         ['xs','sm','md','lg'].map(function(size){
12460             if (settings[size]) {
12461                 cfg.cls += ' col-' + size + '-' + settings[size];
12462             }
12463         });
12464         
12465         return cfg;
12466         
12467     },
12468     
12469     _initEventsCalled : false,
12470     
12471     // private
12472     initEvents: function()
12473     {
12474         
12475         if (this._initEventsCalled) { // as we call render... prevent looping...
12476             return;
12477         }
12478         this._initEventsCalled = true;
12479         
12480         if (!this.store) {
12481             throw "can not find store for combo";
12482         }
12483         
12484         this.store = Roo.factory(this.store, Roo.data);
12485         
12486         // if we are building from html. then this element is so complex, that we can not really
12487         // use the rendered HTML.
12488         // so we have to trash and replace the previous code.
12489         if (Roo.XComponent.build_from_html) {
12490             
12491             // remove this element....
12492             var e = this.el.dom, k=0;
12493             while (e ) { e = e.previousSibling;  ++k;}
12494
12495             this.el.remove();
12496             
12497             this.el=false;
12498             this.rendered = false;
12499             
12500             this.render(this.parent().getChildContainer(true), k);
12501             
12502             
12503             
12504         }
12505         
12506         
12507         /*
12508          * Touch Devices
12509          */
12510         
12511         if(Roo.isTouch && this.mobileTouchView){
12512             this.initTouchView();
12513             return;
12514         }
12515         
12516         if(this.tickable){
12517             this.initTickableEvents();
12518             return;
12519         }
12520         
12521         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12522         
12523         if(this.hiddenName){
12524             
12525             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12526             
12527             this.hiddenField.dom.value =
12528                 this.hiddenValue !== undefined ? this.hiddenValue :
12529                 this.value !== undefined ? this.value : '';
12530
12531             // prevent input submission
12532             this.el.dom.removeAttribute('name');
12533             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12534              
12535              
12536         }
12537         //if(Roo.isGecko){
12538         //    this.el.dom.setAttribute('autocomplete', 'off');
12539         //}
12540         
12541         var cls = 'x-combo-list';
12542         
12543         //this.list = new Roo.Layer({
12544         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12545         //});
12546         
12547         var _this = this;
12548         
12549         (function(){
12550             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12551             _this.list.setWidth(lw);
12552         }).defer(100);
12553         
12554         this.list.on('mouseover', this.onViewOver, this);
12555         this.list.on('mousemove', this.onViewMove, this);
12556         
12557         this.list.on('scroll', this.onViewScroll, this);
12558         
12559         /*
12560         this.list.swallowEvent('mousewheel');
12561         this.assetHeight = 0;
12562
12563         if(this.title){
12564             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12565             this.assetHeight += this.header.getHeight();
12566         }
12567
12568         this.innerList = this.list.createChild({cls:cls+'-inner'});
12569         this.innerList.on('mouseover', this.onViewOver, this);
12570         this.innerList.on('mousemove', this.onViewMove, this);
12571         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12572         
12573         if(this.allowBlank && !this.pageSize && !this.disableClear){
12574             this.footer = this.list.createChild({cls:cls+'-ft'});
12575             this.pageTb = new Roo.Toolbar(this.footer);
12576            
12577         }
12578         if(this.pageSize){
12579             this.footer = this.list.createChild({cls:cls+'-ft'});
12580             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12581                     {pageSize: this.pageSize});
12582             
12583         }
12584         
12585         if (this.pageTb && this.allowBlank && !this.disableClear) {
12586             var _this = this;
12587             this.pageTb.add(new Roo.Toolbar.Fill(), {
12588                 cls: 'x-btn-icon x-btn-clear',
12589                 text: '&#160;',
12590                 handler: function()
12591                 {
12592                     _this.collapse();
12593                     _this.clearValue();
12594                     _this.onSelect(false, -1);
12595                 }
12596             });
12597         }
12598         if (this.footer) {
12599             this.assetHeight += this.footer.getHeight();
12600         }
12601         */
12602             
12603         if(!this.tpl){
12604             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12605         }
12606
12607         this.view = new Roo.View(this.list, this.tpl, {
12608             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12609         });
12610         //this.view.wrapEl.setDisplayed(false);
12611         this.view.on('click', this.onViewClick, this);
12612         
12613         
12614         
12615         this.store.on('beforeload', this.onBeforeLoad, this);
12616         this.store.on('load', this.onLoad, this);
12617         this.store.on('loadexception', this.onLoadException, this);
12618         /*
12619         if(this.resizable){
12620             this.resizer = new Roo.Resizable(this.list,  {
12621                pinned:true, handles:'se'
12622             });
12623             this.resizer.on('resize', function(r, w, h){
12624                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12625                 this.listWidth = w;
12626                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12627                 this.restrictHeight();
12628             }, this);
12629             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12630         }
12631         */
12632         if(!this.editable){
12633             this.editable = true;
12634             this.setEditable(false);
12635         }
12636         
12637         /*
12638         
12639         if (typeof(this.events.add.listeners) != 'undefined') {
12640             
12641             this.addicon = this.wrap.createChild(
12642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12643        
12644             this.addicon.on('click', function(e) {
12645                 this.fireEvent('add', this);
12646             }, this);
12647         }
12648         if (typeof(this.events.edit.listeners) != 'undefined') {
12649             
12650             this.editicon = this.wrap.createChild(
12651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12652             if (this.addicon) {
12653                 this.editicon.setStyle('margin-left', '40px');
12654             }
12655             this.editicon.on('click', function(e) {
12656                 
12657                 // we fire even  if inothing is selected..
12658                 this.fireEvent('edit', this, this.lastData );
12659                 
12660             }, this);
12661         }
12662         */
12663         
12664         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12665             "up" : function(e){
12666                 this.inKeyMode = true;
12667                 this.selectPrev();
12668             },
12669
12670             "down" : function(e){
12671                 if(!this.isExpanded()){
12672                     this.onTriggerClick();
12673                 }else{
12674                     this.inKeyMode = true;
12675                     this.selectNext();
12676                 }
12677             },
12678
12679             "enter" : function(e){
12680 //                this.onViewClick();
12681                 //return true;
12682                 this.collapse();
12683                 
12684                 if(this.fireEvent("specialkey", this, e)){
12685                     this.onViewClick(false);
12686                 }
12687                 
12688                 return true;
12689             },
12690
12691             "esc" : function(e){
12692                 this.collapse();
12693             },
12694
12695             "tab" : function(e){
12696                 this.collapse();
12697                 
12698                 if(this.fireEvent("specialkey", this, e)){
12699                     this.onViewClick(false);
12700                 }
12701                 
12702                 return true;
12703             },
12704
12705             scope : this,
12706
12707             doRelay : function(foo, bar, hname){
12708                 if(hname == 'down' || this.scope.isExpanded()){
12709                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12710                 }
12711                 return true;
12712             },
12713
12714             forceKeyDown: true
12715         });
12716         
12717         
12718         this.queryDelay = Math.max(this.queryDelay || 10,
12719                 this.mode == 'local' ? 10 : 250);
12720         
12721         
12722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12723         
12724         if(this.typeAhead){
12725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12726         }
12727         if(this.editable !== false){
12728             this.inputEl().on("keyup", this.onKeyUp, this);
12729         }
12730         if(this.forceSelection){
12731             this.inputEl().on('blur', this.doForce, this);
12732         }
12733         
12734         if(this.multiple){
12735             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12736             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12737         }
12738     },
12739     
12740     initTickableEvents: function()
12741     {   
12742         this.createList();
12743         
12744         if(this.hiddenName){
12745             
12746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12747             
12748             this.hiddenField.dom.value =
12749                 this.hiddenValue !== undefined ? this.hiddenValue :
12750                 this.value !== undefined ? this.value : '';
12751
12752             // prevent input submission
12753             this.el.dom.removeAttribute('name');
12754             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12755              
12756              
12757         }
12758         
12759 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12760         
12761         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12762         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12763         if(this.triggerList){
12764             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12765         }
12766          
12767         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12768         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12769         
12770         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12771         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12772         
12773         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12774         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12775         
12776         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12777         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12778         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12779         
12780         this.okBtn.hide();
12781         this.cancelBtn.hide();
12782         
12783         var _this = this;
12784         
12785         (function(){
12786             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12787             _this.list.setWidth(lw);
12788         }).defer(100);
12789         
12790         this.list.on('mouseover', this.onViewOver, this);
12791         this.list.on('mousemove', this.onViewMove, this);
12792         
12793         this.list.on('scroll', this.onViewScroll, this);
12794         
12795         if(!this.tpl){
12796             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12797         }
12798
12799         this.view = new Roo.View(this.list, this.tpl, {
12800             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12801         });
12802         
12803         //this.view.wrapEl.setDisplayed(false);
12804         this.view.on('click', this.onViewClick, this);
12805         
12806         
12807         
12808         this.store.on('beforeload', this.onBeforeLoad, this);
12809         this.store.on('load', this.onLoad, this);
12810         this.store.on('loadexception', this.onLoadException, this);
12811         
12812         if(this.editable){
12813             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12814                 "up" : function(e){
12815                     this.inKeyMode = true;
12816                     this.selectPrev();
12817                 },
12818
12819                 "down" : function(e){
12820                     this.inKeyMode = true;
12821                     this.selectNext();
12822                 },
12823
12824                 "enter" : function(e){
12825                     if(this.fireEvent("specialkey", this, e)){
12826                         this.onViewClick(false);
12827                     }
12828                     
12829                     return true;
12830                 },
12831
12832                 "esc" : function(e){
12833                     this.onTickableFooterButtonClick(e, false, false);
12834                 },
12835
12836                 "tab" : function(e){
12837                     this.fireEvent("specialkey", this, e);
12838                     
12839                     this.onTickableFooterButtonClick(e, false, false);
12840                     
12841                     return true;
12842                 },
12843
12844                 scope : this,
12845
12846                 doRelay : function(e, fn, key){
12847                     if(this.scope.isExpanded()){
12848                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12849                     }
12850                     return true;
12851                 },
12852
12853                 forceKeyDown: true
12854             });
12855         }
12856         
12857         this.queryDelay = Math.max(this.queryDelay || 10,
12858                 this.mode == 'local' ? 10 : 250);
12859         
12860         
12861         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12862         
12863         if(this.typeAhead){
12864             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12865         }
12866         
12867         if(this.editable !== false){
12868             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12869         }
12870         
12871     },
12872
12873     onDestroy : function(){
12874         if(this.view){
12875             this.view.setStore(null);
12876             this.view.el.removeAllListeners();
12877             this.view.el.remove();
12878             this.view.purgeListeners();
12879         }
12880         if(this.list){
12881             this.list.dom.innerHTML  = '';
12882         }
12883         
12884         if(this.store){
12885             this.store.un('beforeload', this.onBeforeLoad, this);
12886             this.store.un('load', this.onLoad, this);
12887             this.store.un('loadexception', this.onLoadException, this);
12888         }
12889         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12890     },
12891
12892     // private
12893     fireKey : function(e){
12894         if(e.isNavKeyPress() && !this.list.isVisible()){
12895             this.fireEvent("specialkey", this, e);
12896         }
12897     },
12898
12899     // private
12900     onResize: function(w, h){
12901 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12902 //        
12903 //        if(typeof w != 'number'){
12904 //            // we do not handle it!?!?
12905 //            return;
12906 //        }
12907 //        var tw = this.trigger.getWidth();
12908 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12909 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12910 //        var x = w - tw;
12911 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12912 //            
12913 //        //this.trigger.setStyle('left', x+'px');
12914 //        
12915 //        if(this.list && this.listWidth === undefined){
12916 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12917 //            this.list.setWidth(lw);
12918 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12919 //        }
12920         
12921     
12922         
12923     },
12924
12925     /**
12926      * Allow or prevent the user from directly editing the field text.  If false is passed,
12927      * the user will only be able to select from the items defined in the dropdown list.  This method
12928      * is the runtime equivalent of setting the 'editable' config option at config time.
12929      * @param {Boolean} value True to allow the user to directly edit the field text
12930      */
12931     setEditable : function(value){
12932         if(value == this.editable){
12933             return;
12934         }
12935         this.editable = value;
12936         if(!value){
12937             this.inputEl().dom.setAttribute('readOnly', true);
12938             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12939             this.inputEl().addClass('x-combo-noedit');
12940         }else{
12941             this.inputEl().dom.setAttribute('readOnly', false);
12942             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12943             this.inputEl().removeClass('x-combo-noedit');
12944         }
12945     },
12946
12947     // private
12948     
12949     onBeforeLoad : function(combo,opts){
12950         if(!this.hasFocus){
12951             return;
12952         }
12953          if (!opts.add) {
12954             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12955          }
12956         this.restrictHeight();
12957         this.selectedIndex = -1;
12958     },
12959
12960     // private
12961     onLoad : function(){
12962         
12963         this.hasQuery = false;
12964         
12965         if(!this.hasFocus){
12966             return;
12967         }
12968         
12969         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12970             this.loading.hide();
12971         }
12972              
12973         if(this.store.getCount() > 0){
12974             this.expand();
12975             this.restrictHeight();
12976             if(this.lastQuery == this.allQuery){
12977                 if(this.editable && !this.tickable){
12978                     this.inputEl().dom.select();
12979                 }
12980                 
12981                 if(
12982                     !this.selectByValue(this.value, true) &&
12983                     this.autoFocus && 
12984                     (
12985                         !this.store.lastOptions ||
12986                         typeof(this.store.lastOptions.add) == 'undefined' || 
12987                         this.store.lastOptions.add != true
12988                     )
12989                 ){
12990                     this.select(0, true);
12991                 }
12992             }else{
12993                 if(this.autoFocus){
12994                     this.selectNext();
12995                 }
12996                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12997                     this.taTask.delay(this.typeAheadDelay);
12998                 }
12999             }
13000         }else{
13001             this.onEmptyResults();
13002         }
13003         
13004         //this.el.focus();
13005     },
13006     // private
13007     onLoadException : function()
13008     {
13009         this.hasQuery = false;
13010         
13011         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13012             this.loading.hide();
13013         }
13014         
13015         if(this.tickable && this.editable){
13016             return;
13017         }
13018         
13019         this.collapse();
13020         // only causes errors at present
13021         //Roo.log(this.store.reader.jsonData);
13022         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13023             // fixme
13024             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13025         //}
13026         
13027         
13028     },
13029     // private
13030     onTypeAhead : function(){
13031         if(this.store.getCount() > 0){
13032             var r = this.store.getAt(0);
13033             var newValue = r.data[this.displayField];
13034             var len = newValue.length;
13035             var selStart = this.getRawValue().length;
13036             
13037             if(selStart != len){
13038                 this.setRawValue(newValue);
13039                 this.selectText(selStart, newValue.length);
13040             }
13041         }
13042     },
13043
13044     // private
13045     onSelect : function(record, index){
13046         
13047         if(this.fireEvent('beforeselect', this, record, index) !== false){
13048         
13049             this.setFromData(index > -1 ? record.data : false);
13050             
13051             this.collapse();
13052             this.fireEvent('select', this, record, index);
13053         }
13054     },
13055
13056     /**
13057      * Returns the currently selected field value or empty string if no value is set.
13058      * @return {String} value The selected value
13059      */
13060     getValue : function(){
13061         
13062         if(this.multiple){
13063             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13064         }
13065         
13066         if(this.valueField){
13067             return typeof this.value != 'undefined' ? this.value : '';
13068         }else{
13069             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13070         }
13071     },
13072
13073     /**
13074      * Clears any text/value currently set in the field
13075      */
13076     clearValue : function(){
13077         if(this.hiddenField){
13078             this.hiddenField.dom.value = '';
13079         }
13080         this.value = '';
13081         this.setRawValue('');
13082         this.lastSelectionText = '';
13083         this.lastData = false;
13084         
13085         var close = this.closeTriggerEl();
13086         
13087         if(close){
13088             close.hide();
13089         }
13090         
13091         this.validate();
13092         
13093     },
13094
13095     /**
13096      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13097      * will be displayed in the field.  If the value does not match the data value of an existing item,
13098      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13099      * Otherwise the field will be blank (although the value will still be set).
13100      * @param {String} value The value to match
13101      */
13102     setValue : function(v){
13103         if(this.multiple){
13104             this.syncValue();
13105             return;
13106         }
13107         
13108         var text = v;
13109         if(this.valueField){
13110             var r = this.findRecord(this.valueField, v);
13111             if(r){
13112                 text = r.data[this.displayField];
13113             }else if(this.valueNotFoundText !== undefined){
13114                 text = this.valueNotFoundText;
13115             }
13116         }
13117         this.lastSelectionText = text;
13118         if(this.hiddenField){
13119             this.hiddenField.dom.value = v;
13120         }
13121         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13122         this.value = v;
13123         
13124         var close = this.closeTriggerEl();
13125         
13126         if(close){
13127             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13128         }
13129         
13130         this.validate();
13131     },
13132     /**
13133      * @property {Object} the last set data for the element
13134      */
13135     
13136     lastData : false,
13137     /**
13138      * Sets the value of the field based on a object which is related to the record format for the store.
13139      * @param {Object} value the value to set as. or false on reset?
13140      */
13141     setFromData : function(o){
13142         
13143         if(this.multiple){
13144             this.addItem(o);
13145             return;
13146         }
13147             
13148         var dv = ''; // display value
13149         var vv = ''; // value value..
13150         this.lastData = o;
13151         if (this.displayField) {
13152             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13153         } else {
13154             // this is an error condition!!!
13155             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13156         }
13157         
13158         if(this.valueField){
13159             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13160         }
13161         
13162         var close = this.closeTriggerEl();
13163         
13164         if(close){
13165             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13166         }
13167         
13168         if(this.hiddenField){
13169             this.hiddenField.dom.value = vv;
13170             
13171             this.lastSelectionText = dv;
13172             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13173             this.value = vv;
13174             return;
13175         }
13176         // no hidden field.. - we store the value in 'value', but still display
13177         // display field!!!!
13178         this.lastSelectionText = dv;
13179         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13180         this.value = vv;
13181         
13182         
13183         
13184     },
13185     // private
13186     reset : function(){
13187         // overridden so that last data is reset..
13188         
13189         if(this.multiple){
13190             this.clearItem();
13191             return;
13192         }
13193         
13194         this.setValue(this.originalValue);
13195         //this.clearInvalid();
13196         this.lastData = false;
13197         if (this.view) {
13198             this.view.clearSelections();
13199         }
13200         
13201         this.validate();
13202     },
13203     // private
13204     findRecord : function(prop, value){
13205         var record;
13206         if(this.store.getCount() > 0){
13207             this.store.each(function(r){
13208                 if(r.data[prop] == value){
13209                     record = r;
13210                     return false;
13211                 }
13212                 return true;
13213             });
13214         }
13215         return record;
13216     },
13217     
13218     getName: function()
13219     {
13220         // returns hidden if it's set..
13221         if (!this.rendered) {return ''};
13222         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13223         
13224     },
13225     // private
13226     onViewMove : function(e, t){
13227         this.inKeyMode = false;
13228     },
13229
13230     // private
13231     onViewOver : function(e, t){
13232         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13233             return;
13234         }
13235         var item = this.view.findItemFromChild(t);
13236         
13237         if(item){
13238             var index = this.view.indexOf(item);
13239             this.select(index, false);
13240         }
13241     },
13242
13243     // private
13244     onViewClick : function(view, doFocus, el, e)
13245     {
13246         var index = this.view.getSelectedIndexes()[0];
13247         
13248         var r = this.store.getAt(index);
13249         
13250         if(this.tickable){
13251             
13252             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13253                 return;
13254             }
13255             
13256             var rm = false;
13257             var _this = this;
13258             
13259             Roo.each(this.tickItems, function(v,k){
13260                 
13261                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13262                     Roo.log(v);
13263                     _this.tickItems.splice(k, 1);
13264                     
13265                     if(typeof(e) == 'undefined' && view == false){
13266                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13267                     }
13268                     
13269                     rm = true;
13270                     return;
13271                 }
13272             });
13273             
13274             if(rm){
13275                 return;
13276             }
13277             
13278             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13279                 this.tickItems.push(r.data);
13280             }
13281             
13282             if(typeof(e) == 'undefined' && view == false){
13283                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13284             }
13285                     
13286             return;
13287         }
13288         
13289         if(r){
13290             this.onSelect(r, index);
13291         }
13292         if(doFocus !== false && !this.blockFocus){
13293             this.inputEl().focus();
13294         }
13295     },
13296
13297     // private
13298     restrictHeight : function(){
13299         //this.innerList.dom.style.height = '';
13300         //var inner = this.innerList.dom;
13301         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13302         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13303         //this.list.beginUpdate();
13304         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13305         this.list.alignTo(this.inputEl(), this.listAlign);
13306         this.list.alignTo(this.inputEl(), this.listAlign);
13307         //this.list.endUpdate();
13308     },
13309
13310     // private
13311     onEmptyResults : function(){
13312         
13313         if(this.tickable && this.editable){
13314             this.restrictHeight();
13315             return;
13316         }
13317         
13318         this.collapse();
13319     },
13320
13321     /**
13322      * Returns true if the dropdown list is expanded, else false.
13323      */
13324     isExpanded : function(){
13325         return this.list.isVisible();
13326     },
13327
13328     /**
13329      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13330      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13331      * @param {String} value The data value of the item to select
13332      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13333      * selected item if it is not currently in view (defaults to true)
13334      * @return {Boolean} True if the value matched an item in the list, else false
13335      */
13336     selectByValue : function(v, scrollIntoView){
13337         if(v !== undefined && v !== null){
13338             var r = this.findRecord(this.valueField || this.displayField, v);
13339             if(r){
13340                 this.select(this.store.indexOf(r), scrollIntoView);
13341                 return true;
13342             }
13343         }
13344         return false;
13345     },
13346
13347     /**
13348      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13349      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13350      * @param {Number} index The zero-based index of the list item to select
13351      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13352      * selected item if it is not currently in view (defaults to true)
13353      */
13354     select : function(index, scrollIntoView){
13355         this.selectedIndex = index;
13356         this.view.select(index);
13357         if(scrollIntoView !== false){
13358             var el = this.view.getNode(index);
13359             /*
13360              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13361              */
13362             if(el){
13363                 this.list.scrollChildIntoView(el, false);
13364             }
13365         }
13366     },
13367
13368     // private
13369     selectNext : function(){
13370         var ct = this.store.getCount();
13371         if(ct > 0){
13372             if(this.selectedIndex == -1){
13373                 this.select(0);
13374             }else if(this.selectedIndex < ct-1){
13375                 this.select(this.selectedIndex+1);
13376             }
13377         }
13378     },
13379
13380     // private
13381     selectPrev : function(){
13382         var ct = this.store.getCount();
13383         if(ct > 0){
13384             if(this.selectedIndex == -1){
13385                 this.select(0);
13386             }else if(this.selectedIndex != 0){
13387                 this.select(this.selectedIndex-1);
13388             }
13389         }
13390     },
13391
13392     // private
13393     onKeyUp : function(e){
13394         if(this.editable !== false && !e.isSpecialKey()){
13395             this.lastKey = e.getKey();
13396             this.dqTask.delay(this.queryDelay);
13397         }
13398     },
13399
13400     // private
13401     validateBlur : function(){
13402         return !this.list || !this.list.isVisible();   
13403     },
13404
13405     // private
13406     initQuery : function(){
13407         
13408         var v = this.getRawValue();
13409         
13410         if(this.tickable && this.editable){
13411             v = this.tickableInputEl().getValue();
13412         }
13413         
13414         this.doQuery(v);
13415     },
13416
13417     // private
13418     doForce : function(){
13419         if(this.inputEl().dom.value.length > 0){
13420             this.inputEl().dom.value =
13421                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13422              
13423         }
13424     },
13425
13426     /**
13427      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13428      * query allowing the query action to be canceled if needed.
13429      * @param {String} query The SQL query to execute
13430      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13431      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13432      * saved in the current store (defaults to false)
13433      */
13434     doQuery : function(q, forceAll){
13435         
13436         if(q === undefined || q === null){
13437             q = '';
13438         }
13439         var qe = {
13440             query: q,
13441             forceAll: forceAll,
13442             combo: this,
13443             cancel:false
13444         };
13445         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13446             return false;
13447         }
13448         q = qe.query;
13449         
13450         forceAll = qe.forceAll;
13451         if(forceAll === true || (q.length >= this.minChars)){
13452             
13453             this.hasQuery = true;
13454             
13455             if(this.lastQuery != q || this.alwaysQuery){
13456                 this.lastQuery = q;
13457                 if(this.mode == 'local'){
13458                     this.selectedIndex = -1;
13459                     if(forceAll){
13460                         this.store.clearFilter();
13461                     }else{
13462                         
13463                         if(this.specialFilter){
13464                             this.fireEvent('specialfilter', this);
13465                             this.onLoad();
13466                             return;
13467                         }
13468                         
13469                         this.store.filter(this.displayField, q);
13470                     }
13471                     
13472                     this.store.fireEvent("datachanged", this.store);
13473                     
13474                     this.onLoad();
13475                     
13476                     
13477                 }else{
13478                     
13479                     this.store.baseParams[this.queryParam] = q;
13480                     
13481                     var options = {params : this.getParams(q)};
13482                     
13483                     if(this.loadNext){
13484                         options.add = true;
13485                         options.params.start = this.page * this.pageSize;
13486                     }
13487                     
13488                     this.store.load(options);
13489                     
13490                     /*
13491                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13492                      *  we should expand the list on onLoad
13493                      *  so command out it
13494                      */
13495 //                    this.expand();
13496                 }
13497             }else{
13498                 this.selectedIndex = -1;
13499                 this.onLoad();   
13500             }
13501         }
13502         
13503         this.loadNext = false;
13504     },
13505     
13506     // private
13507     getParams : function(q){
13508         var p = {};
13509         //p[this.queryParam] = q;
13510         
13511         if(this.pageSize){
13512             p.start = 0;
13513             p.limit = this.pageSize;
13514         }
13515         return p;
13516     },
13517
13518     /**
13519      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13520      */
13521     collapse : function(){
13522         if(!this.isExpanded()){
13523             return;
13524         }
13525         
13526         this.list.hide();
13527         
13528         if(this.tickable){
13529             this.hasFocus = false;
13530             this.okBtn.hide();
13531             this.cancelBtn.hide();
13532             this.trigger.show();
13533             
13534             if(this.editable){
13535                 this.tickableInputEl().dom.value = '';
13536                 this.tickableInputEl().blur();
13537             }
13538             
13539         }
13540         
13541         Roo.get(document).un('mousedown', this.collapseIf, this);
13542         Roo.get(document).un('mousewheel', this.collapseIf, this);
13543         if (!this.editable) {
13544             Roo.get(document).un('keydown', this.listKeyPress, this);
13545         }
13546         this.fireEvent('collapse', this);
13547         
13548         this.validate();
13549     },
13550
13551     // private
13552     collapseIf : function(e){
13553         var in_combo  = e.within(this.el);
13554         var in_list =  e.within(this.list);
13555         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13556         
13557         if (in_combo || in_list || is_list) {
13558             //e.stopPropagation();
13559             return;
13560         }
13561         
13562         if(this.tickable){
13563             this.onTickableFooterButtonClick(e, false, false);
13564         }
13565
13566         this.collapse();
13567         
13568     },
13569
13570     /**
13571      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13572      */
13573     expand : function(){
13574        
13575         if(this.isExpanded() || !this.hasFocus){
13576             return;
13577         }
13578         
13579         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13580         this.list.setWidth(lw);
13581         
13582         
13583          Roo.log('expand');
13584         
13585         this.list.show();
13586         
13587         this.restrictHeight();
13588         
13589         if(this.tickable){
13590             
13591             this.tickItems = Roo.apply([], this.item);
13592             
13593             this.okBtn.show();
13594             this.cancelBtn.show();
13595             this.trigger.hide();
13596             
13597             if(this.editable){
13598                 this.tickableInputEl().focus();
13599             }
13600             
13601         }
13602         
13603         Roo.get(document).on('mousedown', this.collapseIf, this);
13604         Roo.get(document).on('mousewheel', this.collapseIf, this);
13605         if (!this.editable) {
13606             Roo.get(document).on('keydown', this.listKeyPress, this);
13607         }
13608         
13609         this.fireEvent('expand', this);
13610     },
13611
13612     // private
13613     // Implements the default empty TriggerField.onTriggerClick function
13614     onTriggerClick : function(e)
13615     {
13616         Roo.log('trigger click');
13617         
13618         if(this.disabled || !this.triggerList){
13619             return;
13620         }
13621         
13622         this.page = 0;
13623         this.loadNext = false;
13624         
13625         if(this.isExpanded()){
13626             this.collapse();
13627             if (!this.blockFocus) {
13628                 this.inputEl().focus();
13629             }
13630             
13631         }else {
13632             this.hasFocus = true;
13633             if(this.triggerAction == 'all') {
13634                 this.doQuery(this.allQuery, true);
13635             } else {
13636                 this.doQuery(this.getRawValue());
13637             }
13638             if (!this.blockFocus) {
13639                 this.inputEl().focus();
13640             }
13641         }
13642     },
13643     
13644     onTickableTriggerClick : function(e)
13645     {
13646         if(this.disabled){
13647             return;
13648         }
13649         
13650         this.page = 0;
13651         this.loadNext = false;
13652         this.hasFocus = true;
13653         
13654         if(this.triggerAction == 'all') {
13655             this.doQuery(this.allQuery, true);
13656         } else {
13657             this.doQuery(this.getRawValue());
13658         }
13659     },
13660     
13661     onSearchFieldClick : function(e)
13662     {
13663         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13664             this.onTickableFooterButtonClick(e, false, false);
13665             return;
13666         }
13667         
13668         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13669             return;
13670         }
13671         
13672         this.page = 0;
13673         this.loadNext = false;
13674         this.hasFocus = true;
13675         
13676         if(this.triggerAction == 'all') {
13677             this.doQuery(this.allQuery, true);
13678         } else {
13679             this.doQuery(this.getRawValue());
13680         }
13681     },
13682     
13683     listKeyPress : function(e)
13684     {
13685         //Roo.log('listkeypress');
13686         // scroll to first matching element based on key pres..
13687         if (e.isSpecialKey()) {
13688             return false;
13689         }
13690         var k = String.fromCharCode(e.getKey()).toUpperCase();
13691         //Roo.log(k);
13692         var match  = false;
13693         var csel = this.view.getSelectedNodes();
13694         var cselitem = false;
13695         if (csel.length) {
13696             var ix = this.view.indexOf(csel[0]);
13697             cselitem  = this.store.getAt(ix);
13698             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13699                 cselitem = false;
13700             }
13701             
13702         }
13703         
13704         this.store.each(function(v) { 
13705             if (cselitem) {
13706                 // start at existing selection.
13707                 if (cselitem.id == v.id) {
13708                     cselitem = false;
13709                 }
13710                 return true;
13711             }
13712                 
13713             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13714                 match = this.store.indexOf(v);
13715                 return false;
13716             }
13717             return true;
13718         }, this);
13719         
13720         if (match === false) {
13721             return true; // no more action?
13722         }
13723         // scroll to?
13724         this.view.select(match);
13725         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13726         sn.scrollIntoView(sn.dom.parentNode, false);
13727     },
13728     
13729     onViewScroll : function(e, t){
13730         
13731         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13732             return;
13733         }
13734         
13735         this.hasQuery = true;
13736         
13737         this.loading = this.list.select('.loading', true).first();
13738         
13739         if(this.loading === null){
13740             this.list.createChild({
13741                 tag: 'div',
13742                 cls: 'loading roo-select2-more-results roo-select2-active',
13743                 html: 'Loading more results...'
13744             });
13745             
13746             this.loading = this.list.select('.loading', true).first();
13747             
13748             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13749             
13750             this.loading.hide();
13751         }
13752         
13753         this.loading.show();
13754         
13755         var _combo = this;
13756         
13757         this.page++;
13758         this.loadNext = true;
13759         
13760         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13761         
13762         return;
13763     },
13764     
13765     addItem : function(o)
13766     {   
13767         var dv = ''; // display value
13768         
13769         if (this.displayField) {
13770             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13771         } else {
13772             // this is an error condition!!!
13773             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13774         }
13775         
13776         if(!dv.length){
13777             return;
13778         }
13779         
13780         var choice = this.choices.createChild({
13781             tag: 'li',
13782             cls: 'roo-select2-search-choice',
13783             cn: [
13784                 {
13785                     tag: 'div',
13786                     html: dv
13787                 },
13788                 {
13789                     tag: 'a',
13790                     href: '#',
13791                     cls: 'roo-select2-search-choice-close',
13792                     tabindex: '-1'
13793                 }
13794             ]
13795             
13796         }, this.searchField);
13797         
13798         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13799         
13800         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13801         
13802         this.item.push(o);
13803         
13804         this.lastData = o;
13805         
13806         this.syncValue();
13807         
13808         this.inputEl().dom.value = '';
13809         
13810         this.validate();
13811     },
13812     
13813     onRemoveItem : function(e, _self, o)
13814     {
13815         e.preventDefault();
13816         
13817         this.lastItem = Roo.apply([], this.item);
13818         
13819         var index = this.item.indexOf(o.data) * 1;
13820         
13821         if( index < 0){
13822             Roo.log('not this item?!');
13823             return;
13824         }
13825         
13826         this.item.splice(index, 1);
13827         o.item.remove();
13828         
13829         this.syncValue();
13830         
13831         this.fireEvent('remove', this, e);
13832         
13833         this.validate();
13834         
13835     },
13836     
13837     syncValue : function()
13838     {
13839         if(!this.item.length){
13840             this.clearValue();
13841             return;
13842         }
13843             
13844         var value = [];
13845         var _this = this;
13846         Roo.each(this.item, function(i){
13847             if(_this.valueField){
13848                 value.push(i[_this.valueField]);
13849                 return;
13850             }
13851
13852             value.push(i);
13853         });
13854
13855         this.value = value.join(',');
13856
13857         if(this.hiddenField){
13858             this.hiddenField.dom.value = this.value;
13859         }
13860         
13861         this.store.fireEvent("datachanged", this.store);
13862         
13863         this.validate();
13864     },
13865     
13866     clearItem : function()
13867     {
13868         if(!this.multiple){
13869             return;
13870         }
13871         
13872         this.item = [];
13873         
13874         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13875            c.remove();
13876         });
13877         
13878         this.syncValue();
13879         
13880         this.validate();
13881         
13882         if(this.tickable && !Roo.isTouch){
13883             this.view.refresh();
13884         }
13885     },
13886     
13887     inputEl: function ()
13888     {
13889         if(Roo.isTouch && this.mobileTouchView){
13890             return this.el.select('input.form-control',true).first();
13891         }
13892         
13893         if(this.tickable){
13894             return this.searchField;
13895         }
13896         
13897         return this.el.select('input.form-control',true).first();
13898     },
13899     
13900     
13901     onTickableFooterButtonClick : function(e, btn, el)
13902     {
13903         e.preventDefault();
13904         
13905         this.lastItem = Roo.apply([], this.item);
13906         
13907         if(btn && btn.name == 'cancel'){
13908             this.tickItems = Roo.apply([], this.item);
13909             this.collapse();
13910             return;
13911         }
13912         
13913         this.clearItem();
13914         
13915         var _this = this;
13916         
13917         Roo.each(this.tickItems, function(o){
13918             _this.addItem(o);
13919         });
13920         
13921         this.collapse();
13922         
13923     },
13924     
13925     validate : function()
13926     {
13927         var v = this.getRawValue();
13928         
13929         if(this.multiple){
13930             v = this.getValue();
13931         }
13932         
13933         if(this.disabled || this.allowBlank || v.length){
13934             this.markValid();
13935             return true;
13936         }
13937         
13938         this.markInvalid();
13939         return false;
13940     },
13941     
13942     tickableInputEl : function()
13943     {
13944         if(!this.tickable || !this.editable){
13945             return this.inputEl();
13946         }
13947         
13948         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13949     },
13950     
13951     
13952     getAutoCreateTouchView : function()
13953     {
13954         var id = Roo.id();
13955         
13956         var cfg = {
13957             cls: 'form-group' //input-group
13958         };
13959         
13960         var input =  {
13961             tag: 'input',
13962             id : id,
13963             type : this.inputType,
13964             cls : 'form-control x-combo-noedit',
13965             autocomplete: 'new-password',
13966             placeholder : this.placeholder || '',
13967             readonly : true
13968         };
13969         
13970         if (this.name) {
13971             input.name = this.name;
13972         }
13973         
13974         if (this.size) {
13975             input.cls += ' input-' + this.size;
13976         }
13977         
13978         if (this.disabled) {
13979             input.disabled = true;
13980         }
13981         
13982         var inputblock = {
13983             cls : '',
13984             cn : [
13985                 input
13986             ]
13987         };
13988         
13989         if(this.before){
13990             inputblock.cls += ' input-group';
13991             
13992             inputblock.cn.unshift({
13993                 tag :'span',
13994                 cls : 'input-group-addon',
13995                 html : this.before
13996             });
13997         }
13998         
13999         if(this.removable && !this.multiple){
14000             inputblock.cls += ' roo-removable';
14001             
14002             inputblock.cn.push({
14003                 tag: 'button',
14004                 html : 'x',
14005                 cls : 'roo-combo-removable-btn close'
14006             });
14007         }
14008
14009         if(this.hasFeedback && !this.allowBlank){
14010             
14011             inputblock.cls += ' has-feedback';
14012             
14013             inputblock.cn.push({
14014                 tag: 'span',
14015                 cls: 'glyphicon form-control-feedback'
14016             });
14017             
14018         }
14019         
14020         if (this.after) {
14021             
14022             inputblock.cls += (this.before) ? '' : ' input-group';
14023             
14024             inputblock.cn.push({
14025                 tag :'span',
14026                 cls : 'input-group-addon',
14027                 html : this.after
14028             });
14029         }
14030
14031         var box = {
14032             tag: 'div',
14033             cn: [
14034                 {
14035                     tag: 'input',
14036                     type : 'hidden',
14037                     cls: 'form-hidden-field'
14038                 },
14039                 inputblock
14040             ]
14041             
14042         };
14043         
14044         if(this.multiple){
14045             box = {
14046                 tag: 'div',
14047                 cn: [
14048                     {
14049                         tag: 'input',
14050                         type : 'hidden',
14051                         cls: 'form-hidden-field'
14052                     },
14053                     {
14054                         tag: 'ul',
14055                         cls: 'roo-select2-choices',
14056                         cn:[
14057                             {
14058                                 tag: 'li',
14059                                 cls: 'roo-select2-search-field',
14060                                 cn: [
14061
14062                                     inputblock
14063                                 ]
14064                             }
14065                         ]
14066                     }
14067                 ]
14068             }
14069         };
14070         
14071         var combobox = {
14072             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14073             cn: [
14074                 box
14075             ]
14076         };
14077         
14078         if(!this.multiple && this.showToggleBtn){
14079             
14080             var caret = {
14081                         tag: 'span',
14082                         cls: 'caret'
14083             };
14084             
14085             if (this.caret != false) {
14086                 caret = {
14087                      tag: 'i',
14088                      cls: 'fa fa-' + this.caret
14089                 };
14090                 
14091             }
14092             
14093             combobox.cn.push({
14094                 tag :'span',
14095                 cls : 'input-group-addon btn dropdown-toggle',
14096                 cn : [
14097                     caret,
14098                     {
14099                         tag: 'span',
14100                         cls: 'combobox-clear',
14101                         cn  : [
14102                             {
14103                                 tag : 'i',
14104                                 cls: 'icon-remove'
14105                             }
14106                         ]
14107                     }
14108                 ]
14109
14110             })
14111         }
14112         
14113         if(this.multiple){
14114             combobox.cls += ' roo-select2-container-multi';
14115         }
14116         
14117         var align = this.labelAlign || this.parentLabelAlign();
14118         
14119         cfg.cn = combobox;
14120         
14121         if(this.fieldLabel.length && this.labelWidth){
14122             
14123             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14124             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14125             
14126             cfg.cn = [
14127                 {
14128                    tag : 'i',
14129                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14130                    tooltip : 'This field is required'
14131                 },
14132                 {
14133                     tag: 'label',
14134                     cls : 'control-label ' + lw,
14135                     html : this.fieldLabel
14136
14137                 },
14138                 {
14139                     cls : cw, 
14140                     cn: [
14141                         combobox
14142                     ]
14143                 }
14144             ];
14145             
14146             if(this.indicatorpos == 'right'){
14147                 cfg.cn = [
14148                     {
14149                         tag: 'label',
14150                         cls : 'control-label ' + lw,
14151                         html : this.fieldLabel
14152
14153                     },
14154                     {
14155                        tag : 'i',
14156                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14157                        tooltip : 'This field is required'
14158                     },
14159                     {
14160                         cls : cw, 
14161                         cn: [
14162                             combobox
14163                         ]
14164                     }
14165                 ];
14166             }
14167         }
14168         
14169         var settings = this;
14170         
14171         ['xs','sm','md','lg'].map(function(size){
14172             if (settings[size]) {
14173                 cfg.cls += ' col-' + size + '-' + settings[size];
14174             }
14175         });
14176         
14177         return cfg;
14178     },
14179     
14180     initTouchView : function()
14181     {
14182         this.renderTouchView();
14183         
14184         this.touchViewEl.on('scroll', function(){
14185             this.el.dom.scrollTop = 0;
14186         }, this);
14187         
14188         this.originalValue = this.getValue();
14189         
14190         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14191         
14192         this.inputEl().on("click", this.showTouchView, this);
14193         this.triggerEl.on("click", this.showTouchView, this);
14194         
14195         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14196         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14197         
14198         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14199         
14200         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14201         this.store.on('load', this.onTouchViewLoad, this);
14202         this.store.on('loadexception', this.onTouchViewLoadException, this);
14203         
14204         if(this.hiddenName){
14205             
14206             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14207             
14208             this.hiddenField.dom.value =
14209                 this.hiddenValue !== undefined ? this.hiddenValue :
14210                 this.value !== undefined ? this.value : '';
14211         
14212             this.el.dom.removeAttribute('name');
14213             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14214         }
14215         
14216         if(this.multiple){
14217             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14218             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14219         }
14220         
14221         if(this.removable && !this.multiple){
14222             var close = this.closeTriggerEl();
14223             if(close){
14224                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14225                 close.on('click', this.removeBtnClick, this, close);
14226             }
14227         }
14228         /*
14229          * fix the bug in Safari iOS8
14230          */
14231         this.inputEl().on("focus", function(e){
14232             document.activeElement.blur();
14233         }, this);
14234         
14235         return;
14236         
14237         
14238     },
14239     
14240     renderTouchView : function()
14241     {
14242         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14243         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14244         
14245         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14246         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14247         
14248         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14249         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14250         this.touchViewBodyEl.setStyle('overflow', 'auto');
14251         
14252         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14253         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14254         
14255         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14256         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14257         
14258     },
14259     
14260     showTouchView : function()
14261     {
14262         if(this.disabled){
14263             return;
14264         }
14265         
14266         this.touchViewHeaderEl.hide();
14267
14268         if(this.modalTitle.length){
14269             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14270             this.touchViewHeaderEl.show();
14271         }
14272
14273         this.touchViewEl.show();
14274
14275         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14276         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14277                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14278
14279         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14280
14281         if(this.modalTitle.length){
14282             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14283         }
14284         
14285         this.touchViewBodyEl.setHeight(bodyHeight);
14286
14287         if(this.animate){
14288             var _this = this;
14289             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14290         }else{
14291             this.touchViewEl.addClass('in');
14292         }
14293
14294         this.doTouchViewQuery();
14295         
14296     },
14297     
14298     hideTouchView : function()
14299     {
14300         this.touchViewEl.removeClass('in');
14301
14302         if(this.animate){
14303             var _this = this;
14304             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14305         }else{
14306             this.touchViewEl.setStyle('display', 'none');
14307         }
14308         
14309     },
14310     
14311     setTouchViewValue : function()
14312     {
14313         if(this.multiple){
14314             this.clearItem();
14315         
14316             var _this = this;
14317
14318             Roo.each(this.tickItems, function(o){
14319                 this.addItem(o);
14320             }, this);
14321         }
14322         
14323         this.hideTouchView();
14324     },
14325     
14326     doTouchViewQuery : function()
14327     {
14328         var qe = {
14329             query: '',
14330             forceAll: true,
14331             combo: this,
14332             cancel:false
14333         };
14334         
14335         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14336             return false;
14337         }
14338         
14339         if(!this.alwaysQuery || this.mode == 'local'){
14340             this.onTouchViewLoad();
14341             return;
14342         }
14343         
14344         this.store.load();
14345     },
14346     
14347     onTouchViewBeforeLoad : function(combo,opts)
14348     {
14349         return;
14350     },
14351
14352     // private
14353     onTouchViewLoad : function()
14354     {
14355         if(this.store.getCount() < 1){
14356             this.onTouchViewEmptyResults();
14357             return;
14358         }
14359         
14360         this.clearTouchView();
14361         
14362         var rawValue = this.getRawValue();
14363         
14364         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14365         
14366         this.tickItems = [];
14367         
14368         this.store.data.each(function(d, rowIndex){
14369             var row = this.touchViewListGroup.createChild(template);
14370             
14371             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14372                 row.addClass(d.data.cls);
14373             }
14374             
14375             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14376                 var cfg = {
14377                     data : d.data,
14378                     html : d.data[this.displayField]
14379                 };
14380                 
14381                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14382                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14383                 }
14384             }
14385             row.removeClass('selected');
14386             if(!this.multiple && this.valueField &&
14387                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14388             {
14389                 // radio buttons..
14390                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14391                 row.addClass('selected');
14392             }
14393             
14394             if(this.multiple && this.valueField &&
14395                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14396             {
14397                 
14398                 // checkboxes...
14399                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14400                 this.tickItems.push(d.data);
14401             }
14402             
14403             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14404             
14405         }, this);
14406         
14407         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14408         
14409         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14410
14411         if(this.modalTitle.length){
14412             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14413         }
14414
14415         var listHeight = this.touchViewListGroup.getHeight();
14416         
14417         var _this = this;
14418         
14419         if(firstChecked && listHeight > bodyHeight){
14420             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14421         }
14422         
14423     },
14424     
14425     onTouchViewLoadException : function()
14426     {
14427         this.hideTouchView();
14428     },
14429     
14430     onTouchViewEmptyResults : function()
14431     {
14432         this.clearTouchView();
14433         
14434         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14435         
14436         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14437         
14438     },
14439     
14440     clearTouchView : function()
14441     {
14442         this.touchViewListGroup.dom.innerHTML = '';
14443     },
14444     
14445     onTouchViewClick : function(e, el, o)
14446     {
14447         e.preventDefault();
14448         
14449         var row = o.row;
14450         var rowIndex = o.rowIndex;
14451         
14452         var r = this.store.getAt(rowIndex);
14453         
14454         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14455             
14456             if(!this.multiple){
14457                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14458                     c.dom.removeAttribute('checked');
14459                 }, this);
14460
14461                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14462
14463                 this.setFromData(r.data);
14464
14465                 var close = this.closeTriggerEl();
14466
14467                 if(close){
14468                     close.show();
14469                 }
14470
14471                 this.hideTouchView();
14472
14473                 this.fireEvent('select', this, r, rowIndex);
14474
14475                 return;
14476             }
14477
14478             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14479                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14480                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14481                 return;
14482             }
14483
14484             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14485             this.addItem(r.data);
14486             this.tickItems.push(r.data);
14487         }
14488     }
14489     
14490
14491     /** 
14492     * @cfg {Boolean} grow 
14493     * @hide 
14494     */
14495     /** 
14496     * @cfg {Number} growMin 
14497     * @hide 
14498     */
14499     /** 
14500     * @cfg {Number} growMax 
14501     * @hide 
14502     */
14503     /**
14504      * @hide
14505      * @method autoSize
14506      */
14507 });
14508
14509 Roo.apply(Roo.bootstrap.ComboBox,  {
14510     
14511     header : {
14512         tag: 'div',
14513         cls: 'modal-header',
14514         cn: [
14515             {
14516                 tag: 'h4',
14517                 cls: 'modal-title'
14518             }
14519         ]
14520     },
14521     
14522     body : {
14523         tag: 'div',
14524         cls: 'modal-body',
14525         cn: [
14526             {
14527                 tag: 'ul',
14528                 cls: 'list-group'
14529             }
14530         ]
14531     },
14532     
14533     listItemRadio : {
14534         tag: 'li',
14535         cls: 'list-group-item',
14536         cn: [
14537             {
14538                 tag: 'span',
14539                 cls: 'roo-combobox-list-group-item-value'
14540             },
14541             {
14542                 tag: 'div',
14543                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14544                 cn: [
14545                     {
14546                         tag: 'input',
14547                         type: 'radio'
14548                     },
14549                     {
14550                         tag: 'label'
14551                     }
14552                 ]
14553             }
14554         ]
14555     },
14556     
14557     listItemCheckbox : {
14558         tag: 'li',
14559         cls: 'list-group-item',
14560         cn: [
14561             {
14562                 tag: 'span',
14563                 cls: 'roo-combobox-list-group-item-value'
14564             },
14565             {
14566                 tag: 'div',
14567                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14568                 cn: [
14569                     {
14570                         tag: 'input',
14571                         type: 'checkbox'
14572                     },
14573                     {
14574                         tag: 'label'
14575                     }
14576                 ]
14577             }
14578         ]
14579     },
14580     
14581     emptyResult : {
14582         tag: 'div',
14583         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14584     },
14585     
14586     footer : {
14587         tag: 'div',
14588         cls: 'modal-footer',
14589         cn: [
14590             {
14591                 tag: 'div',
14592                 cls: 'row',
14593                 cn: [
14594                     {
14595                         tag: 'div',
14596                         cls: 'col-xs-6 text-left',
14597                         cn: {
14598                             tag: 'button',
14599                             cls: 'btn btn-danger roo-touch-view-cancel',
14600                             html: 'Cancel'
14601                         }
14602                     },
14603                     {
14604                         tag: 'div',
14605                         cls: 'col-xs-6 text-right',
14606                         cn: {
14607                             tag: 'button',
14608                             cls: 'btn btn-success roo-touch-view-ok',
14609                             html: 'OK'
14610                         }
14611                     }
14612                 ]
14613             }
14614         ]
14615         
14616     }
14617 });
14618
14619 Roo.apply(Roo.bootstrap.ComboBox,  {
14620     
14621     touchViewTemplate : {
14622         tag: 'div',
14623         cls: 'modal fade roo-combobox-touch-view',
14624         cn: [
14625             {
14626                 tag: 'div',
14627                 cls: 'modal-dialog',
14628                 style : 'position:fixed', // we have to fix position....
14629                 cn: [
14630                     {
14631                         tag: 'div',
14632                         cls: 'modal-content',
14633                         cn: [
14634                             Roo.bootstrap.ComboBox.header,
14635                             Roo.bootstrap.ComboBox.body,
14636                             Roo.bootstrap.ComboBox.footer
14637                         ]
14638                     }
14639                 ]
14640             }
14641         ]
14642     }
14643 });/*
14644  * Based on:
14645  * Ext JS Library 1.1.1
14646  * Copyright(c) 2006-2007, Ext JS, LLC.
14647  *
14648  * Originally Released Under LGPL - original licence link has changed is not relivant.
14649  *
14650  * Fork - LGPL
14651  * <script type="text/javascript">
14652  */
14653
14654 /**
14655  * @class Roo.View
14656  * @extends Roo.util.Observable
14657  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14658  * This class also supports single and multi selection modes. <br>
14659  * Create a data model bound view:
14660  <pre><code>
14661  var store = new Roo.data.Store(...);
14662
14663  var view = new Roo.View({
14664     el : "my-element",
14665     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14666  
14667     singleSelect: true,
14668     selectedClass: "ydataview-selected",
14669     store: store
14670  });
14671
14672  // listen for node click?
14673  view.on("click", function(vw, index, node, e){
14674  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14675  });
14676
14677  // load XML data
14678  dataModel.load("foobar.xml");
14679  </code></pre>
14680  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14681  * <br><br>
14682  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14683  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14684  * 
14685  * Note: old style constructor is still suported (container, template, config)
14686  * 
14687  * @constructor
14688  * Create a new View
14689  * @param {Object} config The config object
14690  * 
14691  */
14692 Roo.View = function(config, depreciated_tpl, depreciated_config){
14693     
14694     this.parent = false;
14695     
14696     if (typeof(depreciated_tpl) == 'undefined') {
14697         // new way.. - universal constructor.
14698         Roo.apply(this, config);
14699         this.el  = Roo.get(this.el);
14700     } else {
14701         // old format..
14702         this.el  = Roo.get(config);
14703         this.tpl = depreciated_tpl;
14704         Roo.apply(this, depreciated_config);
14705     }
14706     this.wrapEl  = this.el.wrap().wrap();
14707     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14708     
14709     
14710     if(typeof(this.tpl) == "string"){
14711         this.tpl = new Roo.Template(this.tpl);
14712     } else {
14713         // support xtype ctors..
14714         this.tpl = new Roo.factory(this.tpl, Roo);
14715     }
14716     
14717     
14718     this.tpl.compile();
14719     
14720     /** @private */
14721     this.addEvents({
14722         /**
14723          * @event beforeclick
14724          * Fires before a click is processed. Returns false to cancel the default action.
14725          * @param {Roo.View} this
14726          * @param {Number} index The index of the target node
14727          * @param {HTMLElement} node The target node
14728          * @param {Roo.EventObject} e The raw event object
14729          */
14730             "beforeclick" : true,
14731         /**
14732          * @event click
14733          * Fires when a template node is clicked.
14734          * @param {Roo.View} this
14735          * @param {Number} index The index of the target node
14736          * @param {HTMLElement} node The target node
14737          * @param {Roo.EventObject} e The raw event object
14738          */
14739             "click" : true,
14740         /**
14741          * @event dblclick
14742          * Fires when a template node is double clicked.
14743          * @param {Roo.View} this
14744          * @param {Number} index The index of the target node
14745          * @param {HTMLElement} node The target node
14746          * @param {Roo.EventObject} e The raw event object
14747          */
14748             "dblclick" : true,
14749         /**
14750          * @event contextmenu
14751          * Fires when a template node is right clicked.
14752          * @param {Roo.View} this
14753          * @param {Number} index The index of the target node
14754          * @param {HTMLElement} node The target node
14755          * @param {Roo.EventObject} e The raw event object
14756          */
14757             "contextmenu" : true,
14758         /**
14759          * @event selectionchange
14760          * Fires when the selected nodes change.
14761          * @param {Roo.View} this
14762          * @param {Array} selections Array of the selected nodes
14763          */
14764             "selectionchange" : true,
14765     
14766         /**
14767          * @event beforeselect
14768          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14769          * @param {Roo.View} this
14770          * @param {HTMLElement} node The node to be selected
14771          * @param {Array} selections Array of currently selected nodes
14772          */
14773             "beforeselect" : true,
14774         /**
14775          * @event preparedata
14776          * Fires on every row to render, to allow you to change the data.
14777          * @param {Roo.View} this
14778          * @param {Object} data to be rendered (change this)
14779          */
14780           "preparedata" : true
14781           
14782           
14783         });
14784
14785
14786
14787     this.el.on({
14788         "click": this.onClick,
14789         "dblclick": this.onDblClick,
14790         "contextmenu": this.onContextMenu,
14791         scope:this
14792     });
14793
14794     this.selections = [];
14795     this.nodes = [];
14796     this.cmp = new Roo.CompositeElementLite([]);
14797     if(this.store){
14798         this.store = Roo.factory(this.store, Roo.data);
14799         this.setStore(this.store, true);
14800     }
14801     
14802     if ( this.footer && this.footer.xtype) {
14803            
14804          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14805         
14806         this.footer.dataSource = this.store;
14807         this.footer.container = fctr;
14808         this.footer = Roo.factory(this.footer, Roo);
14809         fctr.insertFirst(this.el);
14810         
14811         // this is a bit insane - as the paging toolbar seems to detach the el..
14812 //        dom.parentNode.parentNode.parentNode
14813          // they get detached?
14814     }
14815     
14816     
14817     Roo.View.superclass.constructor.call(this);
14818     
14819     
14820 };
14821
14822 Roo.extend(Roo.View, Roo.util.Observable, {
14823     
14824      /**
14825      * @cfg {Roo.data.Store} store Data store to load data from.
14826      */
14827     store : false,
14828     
14829     /**
14830      * @cfg {String|Roo.Element} el The container element.
14831      */
14832     el : '',
14833     
14834     /**
14835      * @cfg {String|Roo.Template} tpl The template used by this View 
14836      */
14837     tpl : false,
14838     /**
14839      * @cfg {String} dataName the named area of the template to use as the data area
14840      *                          Works with domtemplates roo-name="name"
14841      */
14842     dataName: false,
14843     /**
14844      * @cfg {String} selectedClass The css class to add to selected nodes
14845      */
14846     selectedClass : "x-view-selected",
14847      /**
14848      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14849      */
14850     emptyText : "",
14851     
14852     /**
14853      * @cfg {String} text to display on mask (default Loading)
14854      */
14855     mask : false,
14856     /**
14857      * @cfg {Boolean} multiSelect Allow multiple selection
14858      */
14859     multiSelect : false,
14860     /**
14861      * @cfg {Boolean} singleSelect Allow single selection
14862      */
14863     singleSelect:  false,
14864     
14865     /**
14866      * @cfg {Boolean} toggleSelect - selecting 
14867      */
14868     toggleSelect : false,
14869     
14870     /**
14871      * @cfg {Boolean} tickable - selecting 
14872      */
14873     tickable : false,
14874     
14875     /**
14876      * Returns the element this view is bound to.
14877      * @return {Roo.Element}
14878      */
14879     getEl : function(){
14880         return this.wrapEl;
14881     },
14882     
14883     
14884
14885     /**
14886      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14887      */
14888     refresh : function(){
14889         //Roo.log('refresh');
14890         var t = this.tpl;
14891         
14892         // if we are using something like 'domtemplate', then
14893         // the what gets used is:
14894         // t.applySubtemplate(NAME, data, wrapping data..)
14895         // the outer template then get' applied with
14896         //     the store 'extra data'
14897         // and the body get's added to the
14898         //      roo-name="data" node?
14899         //      <span class='roo-tpl-{name}'></span> ?????
14900         
14901         
14902         
14903         this.clearSelections();
14904         this.el.update("");
14905         var html = [];
14906         var records = this.store.getRange();
14907         if(records.length < 1) {
14908             
14909             // is this valid??  = should it render a template??
14910             
14911             this.el.update(this.emptyText);
14912             return;
14913         }
14914         var el = this.el;
14915         if (this.dataName) {
14916             this.el.update(t.apply(this.store.meta)); //????
14917             el = this.el.child('.roo-tpl-' + this.dataName);
14918         }
14919         
14920         for(var i = 0, len = records.length; i < len; i++){
14921             var data = this.prepareData(records[i].data, i, records[i]);
14922             this.fireEvent("preparedata", this, data, i, records[i]);
14923             
14924             var d = Roo.apply({}, data);
14925             
14926             if(this.tickable){
14927                 Roo.apply(d, {'roo-id' : Roo.id()});
14928                 
14929                 var _this = this;
14930             
14931                 Roo.each(this.parent.item, function(item){
14932                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14933                         return;
14934                     }
14935                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14936                 });
14937             }
14938             
14939             html[html.length] = Roo.util.Format.trim(
14940                 this.dataName ?
14941                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14942                     t.apply(d)
14943             );
14944         }
14945         
14946         
14947         
14948         el.update(html.join(""));
14949         this.nodes = el.dom.childNodes;
14950         this.updateIndexes(0);
14951     },
14952     
14953
14954     /**
14955      * Function to override to reformat the data that is sent to
14956      * the template for each node.
14957      * DEPRICATED - use the preparedata event handler.
14958      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14959      * a JSON object for an UpdateManager bound view).
14960      */
14961     prepareData : function(data, index, record)
14962     {
14963         this.fireEvent("preparedata", this, data, index, record);
14964         return data;
14965     },
14966
14967     onUpdate : function(ds, record){
14968         // Roo.log('on update');   
14969         this.clearSelections();
14970         var index = this.store.indexOf(record);
14971         var n = this.nodes[index];
14972         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14973         n.parentNode.removeChild(n);
14974         this.updateIndexes(index, index);
14975     },
14976
14977     
14978     
14979 // --------- FIXME     
14980     onAdd : function(ds, records, index)
14981     {
14982         //Roo.log(['on Add', ds, records, index] );        
14983         this.clearSelections();
14984         if(this.nodes.length == 0){
14985             this.refresh();
14986             return;
14987         }
14988         var n = this.nodes[index];
14989         for(var i = 0, len = records.length; i < len; i++){
14990             var d = this.prepareData(records[i].data, i, records[i]);
14991             if(n){
14992                 this.tpl.insertBefore(n, d);
14993             }else{
14994                 
14995                 this.tpl.append(this.el, d);
14996             }
14997         }
14998         this.updateIndexes(index);
14999     },
15000
15001     onRemove : function(ds, record, index){
15002        // Roo.log('onRemove');
15003         this.clearSelections();
15004         var el = this.dataName  ?
15005             this.el.child('.roo-tpl-' + this.dataName) :
15006             this.el; 
15007         
15008         el.dom.removeChild(this.nodes[index]);
15009         this.updateIndexes(index);
15010     },
15011
15012     /**
15013      * Refresh an individual node.
15014      * @param {Number} index
15015      */
15016     refreshNode : function(index){
15017         this.onUpdate(this.store, this.store.getAt(index));
15018     },
15019
15020     updateIndexes : function(startIndex, endIndex){
15021         var ns = this.nodes;
15022         startIndex = startIndex || 0;
15023         endIndex = endIndex || ns.length - 1;
15024         for(var i = startIndex; i <= endIndex; i++){
15025             ns[i].nodeIndex = i;
15026         }
15027     },
15028
15029     /**
15030      * Changes the data store this view uses and refresh the view.
15031      * @param {Store} store
15032      */
15033     setStore : function(store, initial){
15034         if(!initial && this.store){
15035             this.store.un("datachanged", this.refresh);
15036             this.store.un("add", this.onAdd);
15037             this.store.un("remove", this.onRemove);
15038             this.store.un("update", this.onUpdate);
15039             this.store.un("clear", this.refresh);
15040             this.store.un("beforeload", this.onBeforeLoad);
15041             this.store.un("load", this.onLoad);
15042             this.store.un("loadexception", this.onLoad);
15043         }
15044         if(store){
15045           
15046             store.on("datachanged", this.refresh, this);
15047             store.on("add", this.onAdd, this);
15048             store.on("remove", this.onRemove, this);
15049             store.on("update", this.onUpdate, this);
15050             store.on("clear", this.refresh, this);
15051             store.on("beforeload", this.onBeforeLoad, this);
15052             store.on("load", this.onLoad, this);
15053             store.on("loadexception", this.onLoad, this);
15054         }
15055         
15056         if(store){
15057             this.refresh();
15058         }
15059     },
15060     /**
15061      * onbeforeLoad - masks the loading area.
15062      *
15063      */
15064     onBeforeLoad : function(store,opts)
15065     {
15066          //Roo.log('onBeforeLoad');   
15067         if (!opts.add) {
15068             this.el.update("");
15069         }
15070         this.el.mask(this.mask ? this.mask : "Loading" ); 
15071     },
15072     onLoad : function ()
15073     {
15074         this.el.unmask();
15075     },
15076     
15077
15078     /**
15079      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15080      * @param {HTMLElement} node
15081      * @return {HTMLElement} The template node
15082      */
15083     findItemFromChild : function(node){
15084         var el = this.dataName  ?
15085             this.el.child('.roo-tpl-' + this.dataName,true) :
15086             this.el.dom; 
15087         
15088         if(!node || node.parentNode == el){
15089                     return node;
15090             }
15091             var p = node.parentNode;
15092             while(p && p != el){
15093             if(p.parentNode == el){
15094                 return p;
15095             }
15096             p = p.parentNode;
15097         }
15098             return null;
15099     },
15100
15101     /** @ignore */
15102     onClick : function(e){
15103         var item = this.findItemFromChild(e.getTarget());
15104         if(item){
15105             var index = this.indexOf(item);
15106             if(this.onItemClick(item, index, e) !== false){
15107                 this.fireEvent("click", this, index, item, e);
15108             }
15109         }else{
15110             this.clearSelections();
15111         }
15112     },
15113
15114     /** @ignore */
15115     onContextMenu : function(e){
15116         var item = this.findItemFromChild(e.getTarget());
15117         if(item){
15118             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15119         }
15120     },
15121
15122     /** @ignore */
15123     onDblClick : function(e){
15124         var item = this.findItemFromChild(e.getTarget());
15125         if(item){
15126             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15127         }
15128     },
15129
15130     onItemClick : function(item, index, e)
15131     {
15132         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15133             return false;
15134         }
15135         if (this.toggleSelect) {
15136             var m = this.isSelected(item) ? 'unselect' : 'select';
15137             //Roo.log(m);
15138             var _t = this;
15139             _t[m](item, true, false);
15140             return true;
15141         }
15142         if(this.multiSelect || this.singleSelect){
15143             if(this.multiSelect && e.shiftKey && this.lastSelection){
15144                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15145             }else{
15146                 this.select(item, this.multiSelect && e.ctrlKey);
15147                 this.lastSelection = item;
15148             }
15149             
15150             if(!this.tickable){
15151                 e.preventDefault();
15152             }
15153             
15154         }
15155         return true;
15156     },
15157
15158     /**
15159      * Get the number of selected nodes.
15160      * @return {Number}
15161      */
15162     getSelectionCount : function(){
15163         return this.selections.length;
15164     },
15165
15166     /**
15167      * Get the currently selected nodes.
15168      * @return {Array} An array of HTMLElements
15169      */
15170     getSelectedNodes : function(){
15171         return this.selections;
15172     },
15173
15174     /**
15175      * Get the indexes of the selected nodes.
15176      * @return {Array}
15177      */
15178     getSelectedIndexes : function(){
15179         var indexes = [], s = this.selections;
15180         for(var i = 0, len = s.length; i < len; i++){
15181             indexes.push(s[i].nodeIndex);
15182         }
15183         return indexes;
15184     },
15185
15186     /**
15187      * Clear all selections
15188      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15189      */
15190     clearSelections : function(suppressEvent){
15191         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15192             this.cmp.elements = this.selections;
15193             this.cmp.removeClass(this.selectedClass);
15194             this.selections = [];
15195             if(!suppressEvent){
15196                 this.fireEvent("selectionchange", this, this.selections);
15197             }
15198         }
15199     },
15200
15201     /**
15202      * Returns true if the passed node is selected
15203      * @param {HTMLElement/Number} node The node or node index
15204      * @return {Boolean}
15205      */
15206     isSelected : function(node){
15207         var s = this.selections;
15208         if(s.length < 1){
15209             return false;
15210         }
15211         node = this.getNode(node);
15212         return s.indexOf(node) !== -1;
15213     },
15214
15215     /**
15216      * Selects nodes.
15217      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15218      * @param {Boolean} keepExisting (optional) true to keep existing selections
15219      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15220      */
15221     select : function(nodeInfo, keepExisting, suppressEvent){
15222         if(nodeInfo instanceof Array){
15223             if(!keepExisting){
15224                 this.clearSelections(true);
15225             }
15226             for(var i = 0, len = nodeInfo.length; i < len; i++){
15227                 this.select(nodeInfo[i], true, true);
15228             }
15229             return;
15230         } 
15231         var node = this.getNode(nodeInfo);
15232         if(!node || this.isSelected(node)){
15233             return; // already selected.
15234         }
15235         if(!keepExisting){
15236             this.clearSelections(true);
15237         }
15238         
15239         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15240             Roo.fly(node).addClass(this.selectedClass);
15241             this.selections.push(node);
15242             if(!suppressEvent){
15243                 this.fireEvent("selectionchange", this, this.selections);
15244             }
15245         }
15246         
15247         
15248     },
15249       /**
15250      * Unselects nodes.
15251      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15252      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15253      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15254      */
15255     unselect : function(nodeInfo, keepExisting, suppressEvent)
15256     {
15257         if(nodeInfo instanceof Array){
15258             Roo.each(this.selections, function(s) {
15259                 this.unselect(s, nodeInfo);
15260             }, this);
15261             return;
15262         }
15263         var node = this.getNode(nodeInfo);
15264         if(!node || !this.isSelected(node)){
15265             //Roo.log("not selected");
15266             return; // not selected.
15267         }
15268         // fireevent???
15269         var ns = [];
15270         Roo.each(this.selections, function(s) {
15271             if (s == node ) {
15272                 Roo.fly(node).removeClass(this.selectedClass);
15273
15274                 return;
15275             }
15276             ns.push(s);
15277         },this);
15278         
15279         this.selections= ns;
15280         this.fireEvent("selectionchange", this, this.selections);
15281     },
15282
15283     /**
15284      * Gets a template node.
15285      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15286      * @return {HTMLElement} The node or null if it wasn't found
15287      */
15288     getNode : function(nodeInfo){
15289         if(typeof nodeInfo == "string"){
15290             return document.getElementById(nodeInfo);
15291         }else if(typeof nodeInfo == "number"){
15292             return this.nodes[nodeInfo];
15293         }
15294         return nodeInfo;
15295     },
15296
15297     /**
15298      * Gets a range template nodes.
15299      * @param {Number} startIndex
15300      * @param {Number} endIndex
15301      * @return {Array} An array of nodes
15302      */
15303     getNodes : function(start, end){
15304         var ns = this.nodes;
15305         start = start || 0;
15306         end = typeof end == "undefined" ? ns.length - 1 : end;
15307         var nodes = [];
15308         if(start <= end){
15309             for(var i = start; i <= end; i++){
15310                 nodes.push(ns[i]);
15311             }
15312         } else{
15313             for(var i = start; i >= end; i--){
15314                 nodes.push(ns[i]);
15315             }
15316         }
15317         return nodes;
15318     },
15319
15320     /**
15321      * Finds the index of the passed node
15322      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15323      * @return {Number} The index of the node or -1
15324      */
15325     indexOf : function(node){
15326         node = this.getNode(node);
15327         if(typeof node.nodeIndex == "number"){
15328             return node.nodeIndex;
15329         }
15330         var ns = this.nodes;
15331         for(var i = 0, len = ns.length; i < len; i++){
15332             if(ns[i] == node){
15333                 return i;
15334             }
15335         }
15336         return -1;
15337     }
15338 });
15339 /*
15340  * - LGPL
15341  *
15342  * based on jquery fullcalendar
15343  * 
15344  */
15345
15346 Roo.bootstrap = Roo.bootstrap || {};
15347 /**
15348  * @class Roo.bootstrap.Calendar
15349  * @extends Roo.bootstrap.Component
15350  * Bootstrap Calendar class
15351  * @cfg {Boolean} loadMask (true|false) default false
15352  * @cfg {Object} header generate the user specific header of the calendar, default false
15353
15354  * @constructor
15355  * Create a new Container
15356  * @param {Object} config The config object
15357  */
15358
15359
15360
15361 Roo.bootstrap.Calendar = function(config){
15362     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15363      this.addEvents({
15364         /**
15365              * @event select
15366              * Fires when a date is selected
15367              * @param {DatePicker} this
15368              * @param {Date} date The selected date
15369              */
15370         'select': true,
15371         /**
15372              * @event monthchange
15373              * Fires when the displayed month changes 
15374              * @param {DatePicker} this
15375              * @param {Date} date The selected month
15376              */
15377         'monthchange': true,
15378         /**
15379              * @event evententer
15380              * Fires when mouse over an event
15381              * @param {Calendar} this
15382              * @param {event} Event
15383              */
15384         'evententer': true,
15385         /**
15386              * @event eventleave
15387              * Fires when the mouse leaves an
15388              * @param {Calendar} this
15389              * @param {event}
15390              */
15391         'eventleave': true,
15392         /**
15393              * @event eventclick
15394              * Fires when the mouse click an
15395              * @param {Calendar} this
15396              * @param {event}
15397              */
15398         'eventclick': true
15399         
15400     });
15401
15402 };
15403
15404 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15405     
15406      /**
15407      * @cfg {Number} startDay
15408      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15409      */
15410     startDay : 0,
15411     
15412     loadMask : false,
15413     
15414     header : false,
15415       
15416     getAutoCreate : function(){
15417         
15418         
15419         var fc_button = function(name, corner, style, content ) {
15420             return Roo.apply({},{
15421                 tag : 'span',
15422                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15423                          (corner.length ?
15424                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15425                             ''
15426                         ),
15427                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15428                 unselectable: 'on'
15429             });
15430         };
15431         
15432         var header = {};
15433         
15434         if(!this.header){
15435             header = {
15436                 tag : 'table',
15437                 cls : 'fc-header',
15438                 style : 'width:100%',
15439                 cn : [
15440                     {
15441                         tag: 'tr',
15442                         cn : [
15443                             {
15444                                 tag : 'td',
15445                                 cls : 'fc-header-left',
15446                                 cn : [
15447                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15448                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15449                                     { tag: 'span', cls: 'fc-header-space' },
15450                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15451
15452
15453                                 ]
15454                             },
15455
15456                             {
15457                                 tag : 'td',
15458                                 cls : 'fc-header-center',
15459                                 cn : [
15460                                     {
15461                                         tag: 'span',
15462                                         cls: 'fc-header-title',
15463                                         cn : {
15464                                             tag: 'H2',
15465                                             html : 'month / year'
15466                                         }
15467                                     }
15468
15469                                 ]
15470                             },
15471                             {
15472                                 tag : 'td',
15473                                 cls : 'fc-header-right',
15474                                 cn : [
15475                               /*      fc_button('month', 'left', '', 'month' ),
15476                                     fc_button('week', '', '', 'week' ),
15477                                     fc_button('day', 'right', '', 'day' )
15478                                 */    
15479
15480                                 ]
15481                             }
15482
15483                         ]
15484                     }
15485                 ]
15486             };
15487         }
15488         
15489         header = this.header;
15490         
15491        
15492         var cal_heads = function() {
15493             var ret = [];
15494             // fixme - handle this.
15495             
15496             for (var i =0; i < Date.dayNames.length; i++) {
15497                 var d = Date.dayNames[i];
15498                 ret.push({
15499                     tag: 'th',
15500                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15501                     html : d.substring(0,3)
15502                 });
15503                 
15504             }
15505             ret[0].cls += ' fc-first';
15506             ret[6].cls += ' fc-last';
15507             return ret;
15508         };
15509         var cal_cell = function(n) {
15510             return  {
15511                 tag: 'td',
15512                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15513                 cn : [
15514                     {
15515                         cn : [
15516                             {
15517                                 cls: 'fc-day-number',
15518                                 html: 'D'
15519                             },
15520                             {
15521                                 cls: 'fc-day-content',
15522                              
15523                                 cn : [
15524                                      {
15525                                         style: 'position: relative;' // height: 17px;
15526                                     }
15527                                 ]
15528                             }
15529                             
15530                             
15531                         ]
15532                     }
15533                 ]
15534                 
15535             }
15536         };
15537         var cal_rows = function() {
15538             
15539             var ret = [];
15540             for (var r = 0; r < 6; r++) {
15541                 var row= {
15542                     tag : 'tr',
15543                     cls : 'fc-week',
15544                     cn : []
15545                 };
15546                 
15547                 for (var i =0; i < Date.dayNames.length; i++) {
15548                     var d = Date.dayNames[i];
15549                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15550
15551                 }
15552                 row.cn[0].cls+=' fc-first';
15553                 row.cn[0].cn[0].style = 'min-height:90px';
15554                 row.cn[6].cls+=' fc-last';
15555                 ret.push(row);
15556                 
15557             }
15558             ret[0].cls += ' fc-first';
15559             ret[4].cls += ' fc-prev-last';
15560             ret[5].cls += ' fc-last';
15561             return ret;
15562             
15563         };
15564         
15565         var cal_table = {
15566             tag: 'table',
15567             cls: 'fc-border-separate',
15568             style : 'width:100%',
15569             cellspacing  : 0,
15570             cn : [
15571                 { 
15572                     tag: 'thead',
15573                     cn : [
15574                         { 
15575                             tag: 'tr',
15576                             cls : 'fc-first fc-last',
15577                             cn : cal_heads()
15578                         }
15579                     ]
15580                 },
15581                 { 
15582                     tag: 'tbody',
15583                     cn : cal_rows()
15584                 }
15585                   
15586             ]
15587         };
15588          
15589          var cfg = {
15590             cls : 'fc fc-ltr',
15591             cn : [
15592                 header,
15593                 {
15594                     cls : 'fc-content',
15595                     style : "position: relative;",
15596                     cn : [
15597                         {
15598                             cls : 'fc-view fc-view-month fc-grid',
15599                             style : 'position: relative',
15600                             unselectable : 'on',
15601                             cn : [
15602                                 {
15603                                     cls : 'fc-event-container',
15604                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15605                                 },
15606                                 cal_table
15607                             ]
15608                         }
15609                     ]
15610     
15611                 }
15612            ] 
15613             
15614         };
15615         
15616          
15617         
15618         return cfg;
15619     },
15620     
15621     
15622     initEvents : function()
15623     {
15624         if(!this.store){
15625             throw "can not find store for calendar";
15626         }
15627         
15628         var mark = {
15629             tag: "div",
15630             cls:"x-dlg-mask",
15631             style: "text-align:center",
15632             cn: [
15633                 {
15634                     tag: "div",
15635                     style: "background-color:white;width:50%;margin:250 auto",
15636                     cn: [
15637                         {
15638                             tag: "img",
15639                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15640                         },
15641                         {
15642                             tag: "span",
15643                             html: "Loading"
15644                         }
15645                         
15646                     ]
15647                 }
15648             ]
15649         };
15650         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15651         
15652         var size = this.el.select('.fc-content', true).first().getSize();
15653         this.maskEl.setSize(size.width, size.height);
15654         this.maskEl.enableDisplayMode("block");
15655         if(!this.loadMask){
15656             this.maskEl.hide();
15657         }
15658         
15659         this.store = Roo.factory(this.store, Roo.data);
15660         this.store.on('load', this.onLoad, this);
15661         this.store.on('beforeload', this.onBeforeLoad, this);
15662         
15663         this.resize();
15664         
15665         this.cells = this.el.select('.fc-day',true);
15666         //Roo.log(this.cells);
15667         this.textNodes = this.el.query('.fc-day-number');
15668         this.cells.addClassOnOver('fc-state-hover');
15669         
15670         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15671         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15672         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15673         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15674         
15675         this.on('monthchange', this.onMonthChange, this);
15676         
15677         this.update(new Date().clearTime());
15678     },
15679     
15680     resize : function() {
15681         var sz  = this.el.getSize();
15682         
15683         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15684         this.el.select('.fc-day-content div',true).setHeight(34);
15685     },
15686     
15687     
15688     // private
15689     showPrevMonth : function(e){
15690         this.update(this.activeDate.add("mo", -1));
15691     },
15692     showToday : function(e){
15693         this.update(new Date().clearTime());
15694     },
15695     // private
15696     showNextMonth : function(e){
15697         this.update(this.activeDate.add("mo", 1));
15698     },
15699
15700     // private
15701     showPrevYear : function(){
15702         this.update(this.activeDate.add("y", -1));
15703     },
15704
15705     // private
15706     showNextYear : function(){
15707         this.update(this.activeDate.add("y", 1));
15708     },
15709
15710     
15711    // private
15712     update : function(date)
15713     {
15714         var vd = this.activeDate;
15715         this.activeDate = date;
15716 //        if(vd && this.el){
15717 //            var t = date.getTime();
15718 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15719 //                Roo.log('using add remove');
15720 //                
15721 //                this.fireEvent('monthchange', this, date);
15722 //                
15723 //                this.cells.removeClass("fc-state-highlight");
15724 //                this.cells.each(function(c){
15725 //                   if(c.dateValue == t){
15726 //                       c.addClass("fc-state-highlight");
15727 //                       setTimeout(function(){
15728 //                            try{c.dom.firstChild.focus();}catch(e){}
15729 //                       }, 50);
15730 //                       return false;
15731 //                   }
15732 //                   return true;
15733 //                });
15734 //                return;
15735 //            }
15736 //        }
15737         
15738         var days = date.getDaysInMonth();
15739         
15740         var firstOfMonth = date.getFirstDateOfMonth();
15741         var startingPos = firstOfMonth.getDay()-this.startDay;
15742         
15743         if(startingPos < this.startDay){
15744             startingPos += 7;
15745         }
15746         
15747         var pm = date.add(Date.MONTH, -1);
15748         var prevStart = pm.getDaysInMonth()-startingPos;
15749 //        
15750         this.cells = this.el.select('.fc-day',true);
15751         this.textNodes = this.el.query('.fc-day-number');
15752         this.cells.addClassOnOver('fc-state-hover');
15753         
15754         var cells = this.cells.elements;
15755         var textEls = this.textNodes;
15756         
15757         Roo.each(cells, function(cell){
15758             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15759         });
15760         
15761         days += startingPos;
15762
15763         // convert everything to numbers so it's fast
15764         var day = 86400000;
15765         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15766         //Roo.log(d);
15767         //Roo.log(pm);
15768         //Roo.log(prevStart);
15769         
15770         var today = new Date().clearTime().getTime();
15771         var sel = date.clearTime().getTime();
15772         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15773         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15774         var ddMatch = this.disabledDatesRE;
15775         var ddText = this.disabledDatesText;
15776         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15777         var ddaysText = this.disabledDaysText;
15778         var format = this.format;
15779         
15780         var setCellClass = function(cal, cell){
15781             cell.row = 0;
15782             cell.events = [];
15783             cell.more = [];
15784             //Roo.log('set Cell Class');
15785             cell.title = "";
15786             var t = d.getTime();
15787             
15788             //Roo.log(d);
15789             
15790             cell.dateValue = t;
15791             if(t == today){
15792                 cell.className += " fc-today";
15793                 cell.className += " fc-state-highlight";
15794                 cell.title = cal.todayText;
15795             }
15796             if(t == sel){
15797                 // disable highlight in other month..
15798                 //cell.className += " fc-state-highlight";
15799                 
15800             }
15801             // disabling
15802             if(t < min) {
15803                 cell.className = " fc-state-disabled";
15804                 cell.title = cal.minText;
15805                 return;
15806             }
15807             if(t > max) {
15808                 cell.className = " fc-state-disabled";
15809                 cell.title = cal.maxText;
15810                 return;
15811             }
15812             if(ddays){
15813                 if(ddays.indexOf(d.getDay()) != -1){
15814                     cell.title = ddaysText;
15815                     cell.className = " fc-state-disabled";
15816                 }
15817             }
15818             if(ddMatch && format){
15819                 var fvalue = d.dateFormat(format);
15820                 if(ddMatch.test(fvalue)){
15821                     cell.title = ddText.replace("%0", fvalue);
15822                     cell.className = " fc-state-disabled";
15823                 }
15824             }
15825             
15826             if (!cell.initialClassName) {
15827                 cell.initialClassName = cell.dom.className;
15828             }
15829             
15830             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15831         };
15832
15833         var i = 0;
15834         
15835         for(; i < startingPos; i++) {
15836             textEls[i].innerHTML = (++prevStart);
15837             d.setDate(d.getDate()+1);
15838             
15839             cells[i].className = "fc-past fc-other-month";
15840             setCellClass(this, cells[i]);
15841         }
15842         
15843         var intDay = 0;
15844         
15845         for(; i < days; i++){
15846             intDay = i - startingPos + 1;
15847             textEls[i].innerHTML = (intDay);
15848             d.setDate(d.getDate()+1);
15849             
15850             cells[i].className = ''; // "x-date-active";
15851             setCellClass(this, cells[i]);
15852         }
15853         var extraDays = 0;
15854         
15855         for(; i < 42; i++) {
15856             textEls[i].innerHTML = (++extraDays);
15857             d.setDate(d.getDate()+1);
15858             
15859             cells[i].className = "fc-future fc-other-month";
15860             setCellClass(this, cells[i]);
15861         }
15862         
15863         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15864         
15865         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15866         
15867         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15868         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15869         
15870         if(totalRows != 6){
15871             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15872             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15873         }
15874         
15875         this.fireEvent('monthchange', this, date);
15876         
15877         
15878         /*
15879         if(!this.internalRender){
15880             var main = this.el.dom.firstChild;
15881             var w = main.offsetWidth;
15882             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15883             Roo.fly(main).setWidth(w);
15884             this.internalRender = true;
15885             // opera does not respect the auto grow header center column
15886             // then, after it gets a width opera refuses to recalculate
15887             // without a second pass
15888             if(Roo.isOpera && !this.secondPass){
15889                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15890                 this.secondPass = true;
15891                 this.update.defer(10, this, [date]);
15892             }
15893         }
15894         */
15895         
15896     },
15897     
15898     findCell : function(dt) {
15899         dt = dt.clearTime().getTime();
15900         var ret = false;
15901         this.cells.each(function(c){
15902             //Roo.log("check " +c.dateValue + '?=' + dt);
15903             if(c.dateValue == dt){
15904                 ret = c;
15905                 return false;
15906             }
15907             return true;
15908         });
15909         
15910         return ret;
15911     },
15912     
15913     findCells : function(ev) {
15914         var s = ev.start.clone().clearTime().getTime();
15915        // Roo.log(s);
15916         var e= ev.end.clone().clearTime().getTime();
15917        // Roo.log(e);
15918         var ret = [];
15919         this.cells.each(function(c){
15920              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15921             
15922             if(c.dateValue > e){
15923                 return ;
15924             }
15925             if(c.dateValue < s){
15926                 return ;
15927             }
15928             ret.push(c);
15929         });
15930         
15931         return ret;    
15932     },
15933     
15934 //    findBestRow: function(cells)
15935 //    {
15936 //        var ret = 0;
15937 //        
15938 //        for (var i =0 ; i < cells.length;i++) {
15939 //            ret  = Math.max(cells[i].rows || 0,ret);
15940 //        }
15941 //        return ret;
15942 //        
15943 //    },
15944     
15945     
15946     addItem : function(ev)
15947     {
15948         // look for vertical location slot in
15949         var cells = this.findCells(ev);
15950         
15951 //        ev.row = this.findBestRow(cells);
15952         
15953         // work out the location.
15954         
15955         var crow = false;
15956         var rows = [];
15957         for(var i =0; i < cells.length; i++) {
15958             
15959             cells[i].row = cells[0].row;
15960             
15961             if(i == 0){
15962                 cells[i].row = cells[i].row + 1;
15963             }
15964             
15965             if (!crow) {
15966                 crow = {
15967                     start : cells[i],
15968                     end :  cells[i]
15969                 };
15970                 continue;
15971             }
15972             if (crow.start.getY() == cells[i].getY()) {
15973                 // on same row.
15974                 crow.end = cells[i];
15975                 continue;
15976             }
15977             // different row.
15978             rows.push(crow);
15979             crow = {
15980                 start: cells[i],
15981                 end : cells[i]
15982             };
15983             
15984         }
15985         
15986         rows.push(crow);
15987         ev.els = [];
15988         ev.rows = rows;
15989         ev.cells = cells;
15990         
15991         cells[0].events.push(ev);
15992         
15993         this.calevents.push(ev);
15994     },
15995     
15996     clearEvents: function() {
15997         
15998         if(!this.calevents){
15999             return;
16000         }
16001         
16002         Roo.each(this.cells.elements, function(c){
16003             c.row = 0;
16004             c.events = [];
16005             c.more = [];
16006         });
16007         
16008         Roo.each(this.calevents, function(e) {
16009             Roo.each(e.els, function(el) {
16010                 el.un('mouseenter' ,this.onEventEnter, this);
16011                 el.un('mouseleave' ,this.onEventLeave, this);
16012                 el.remove();
16013             },this);
16014         },this);
16015         
16016         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16017             e.remove();
16018         });
16019         
16020     },
16021     
16022     renderEvents: function()
16023     {   
16024         var _this = this;
16025         
16026         this.cells.each(function(c) {
16027             
16028             if(c.row < 5){
16029                 return;
16030             }
16031             
16032             var ev = c.events;
16033             
16034             var r = 4;
16035             if(c.row != c.events.length){
16036                 r = 4 - (4 - (c.row - c.events.length));
16037             }
16038             
16039             c.events = ev.slice(0, r);
16040             c.more = ev.slice(r);
16041             
16042             if(c.more.length && c.more.length == 1){
16043                 c.events.push(c.more.pop());
16044             }
16045             
16046             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16047             
16048         });
16049             
16050         this.cells.each(function(c) {
16051             
16052             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16053             
16054             
16055             for (var e = 0; e < c.events.length; e++){
16056                 var ev = c.events[e];
16057                 var rows = ev.rows;
16058                 
16059                 for(var i = 0; i < rows.length; i++) {
16060                 
16061                     // how many rows should it span..
16062
16063                     var  cfg = {
16064                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16065                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16066
16067                         unselectable : "on",
16068                         cn : [
16069                             {
16070                                 cls: 'fc-event-inner',
16071                                 cn : [
16072     //                                {
16073     //                                  tag:'span',
16074     //                                  cls: 'fc-event-time',
16075     //                                  html : cells.length > 1 ? '' : ev.time
16076     //                                },
16077                                     {
16078                                       tag:'span',
16079                                       cls: 'fc-event-title',
16080                                       html : String.format('{0}', ev.title)
16081                                     }
16082
16083
16084                                 ]
16085                             },
16086                             {
16087                                 cls: 'ui-resizable-handle ui-resizable-e',
16088                                 html : '&nbsp;&nbsp;&nbsp'
16089                             }
16090
16091                         ]
16092                     };
16093
16094                     if (i == 0) {
16095                         cfg.cls += ' fc-event-start';
16096                     }
16097                     if ((i+1) == rows.length) {
16098                         cfg.cls += ' fc-event-end';
16099                     }
16100
16101                     var ctr = _this.el.select('.fc-event-container',true).first();
16102                     var cg = ctr.createChild(cfg);
16103
16104                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16105                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16106
16107                     var r = (c.more.length) ? 1 : 0;
16108                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16109                     cg.setWidth(ebox.right - sbox.x -2);
16110
16111                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16112                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16113                     cg.on('click', _this.onEventClick, _this, ev);
16114
16115                     ev.els.push(cg);
16116                     
16117                 }
16118                 
16119             }
16120             
16121             
16122             if(c.more.length){
16123                 var  cfg = {
16124                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16125                     style : 'position: absolute',
16126                     unselectable : "on",
16127                     cn : [
16128                         {
16129                             cls: 'fc-event-inner',
16130                             cn : [
16131                                 {
16132                                   tag:'span',
16133                                   cls: 'fc-event-title',
16134                                   html : 'More'
16135                                 }
16136
16137
16138                             ]
16139                         },
16140                         {
16141                             cls: 'ui-resizable-handle ui-resizable-e',
16142                             html : '&nbsp;&nbsp;&nbsp'
16143                         }
16144
16145                     ]
16146                 };
16147
16148                 var ctr = _this.el.select('.fc-event-container',true).first();
16149                 var cg = ctr.createChild(cfg);
16150
16151                 var sbox = c.select('.fc-day-content',true).first().getBox();
16152                 var ebox = c.select('.fc-day-content',true).first().getBox();
16153                 //Roo.log(cg);
16154                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16155                 cg.setWidth(ebox.right - sbox.x -2);
16156
16157                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16158                 
16159             }
16160             
16161         });
16162         
16163         
16164         
16165     },
16166     
16167     onEventEnter: function (e, el,event,d) {
16168         this.fireEvent('evententer', this, el, event);
16169     },
16170     
16171     onEventLeave: function (e, el,event,d) {
16172         this.fireEvent('eventleave', this, el, event);
16173     },
16174     
16175     onEventClick: function (e, el,event,d) {
16176         this.fireEvent('eventclick', this, el, event);
16177     },
16178     
16179     onMonthChange: function () {
16180         this.store.load();
16181     },
16182     
16183     onMoreEventClick: function(e, el, more)
16184     {
16185         var _this = this;
16186         
16187         this.calpopover.placement = 'right';
16188         this.calpopover.setTitle('More');
16189         
16190         this.calpopover.setContent('');
16191         
16192         var ctr = this.calpopover.el.select('.popover-content', true).first();
16193         
16194         Roo.each(more, function(m){
16195             var cfg = {
16196                 cls : 'fc-event-hori fc-event-draggable',
16197                 html : m.title
16198             };
16199             var cg = ctr.createChild(cfg);
16200             
16201             cg.on('click', _this.onEventClick, _this, m);
16202         });
16203         
16204         this.calpopover.show(el);
16205         
16206         
16207     },
16208     
16209     onLoad: function () 
16210     {   
16211         this.calevents = [];
16212         var cal = this;
16213         
16214         if(this.store.getCount() > 0){
16215             this.store.data.each(function(d){
16216                cal.addItem({
16217                     id : d.data.id,
16218                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16219                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16220                     time : d.data.start_time,
16221                     title : d.data.title,
16222                     description : d.data.description,
16223                     venue : d.data.venue
16224                 });
16225             });
16226         }
16227         
16228         this.renderEvents();
16229         
16230         if(this.calevents.length && this.loadMask){
16231             this.maskEl.hide();
16232         }
16233     },
16234     
16235     onBeforeLoad: function()
16236     {
16237         this.clearEvents();
16238         if(this.loadMask){
16239             this.maskEl.show();
16240         }
16241     }
16242 });
16243
16244  
16245  /*
16246  * - LGPL
16247  *
16248  * element
16249  * 
16250  */
16251
16252 /**
16253  * @class Roo.bootstrap.Popover
16254  * @extends Roo.bootstrap.Component
16255  * Bootstrap Popover class
16256  * @cfg {String} html contents of the popover   (or false to use children..)
16257  * @cfg {String} title of popover (or false to hide)
16258  * @cfg {String} placement how it is placed
16259  * @cfg {String} trigger click || hover (or false to trigger manually)
16260  * @cfg {String} over what (parent or false to trigger manually.)
16261  * @cfg {Number} delay - delay before showing
16262  
16263  * @constructor
16264  * Create a new Popover
16265  * @param {Object} config The config object
16266  */
16267
16268 Roo.bootstrap.Popover = function(config){
16269     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16270     
16271     this.addEvents({
16272         // raw events
16273          /**
16274          * @event show
16275          * After the popover show
16276          * 
16277          * @param {Roo.bootstrap.Popover} this
16278          */
16279         "show" : true,
16280         /**
16281          * @event hide
16282          * After the popover hide
16283          * 
16284          * @param {Roo.bootstrap.Popover} this
16285          */
16286         "hide" : true
16287     });
16288 };
16289
16290 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16291     
16292     title: 'Fill in a title',
16293     html: false,
16294     
16295     placement : 'right',
16296     trigger : 'hover', // hover
16297     
16298     delay : 0,
16299     
16300     over: 'parent',
16301     
16302     can_build_overlaid : false,
16303     
16304     getChildContainer : function()
16305     {
16306         return this.el.select('.popover-content',true).first();
16307     },
16308     
16309     getAutoCreate : function(){
16310          
16311         var cfg = {
16312            cls : 'popover roo-dynamic',
16313            style: 'display:block',
16314            cn : [
16315                 {
16316                     cls : 'arrow'
16317                 },
16318                 {
16319                     cls : 'popover-inner',
16320                     cn : [
16321                         {
16322                             tag: 'h3',
16323                             cls: 'popover-title',
16324                             html : this.title
16325                         },
16326                         {
16327                             cls : 'popover-content',
16328                             html : this.html
16329                         }
16330                     ]
16331                     
16332                 }
16333            ]
16334         };
16335         
16336         return cfg;
16337     },
16338     setTitle: function(str)
16339     {
16340         this.title = str;
16341         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16342     },
16343     setContent: function(str)
16344     {
16345         this.html = str;
16346         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16347     },
16348     // as it get's added to the bottom of the page.
16349     onRender : function(ct, position)
16350     {
16351         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16352         if(!this.el){
16353             var cfg = Roo.apply({},  this.getAutoCreate());
16354             cfg.id = Roo.id();
16355             
16356             if (this.cls) {
16357                 cfg.cls += ' ' + this.cls;
16358             }
16359             if (this.style) {
16360                 cfg.style = this.style;
16361             }
16362             //Roo.log("adding to ");
16363             this.el = Roo.get(document.body).createChild(cfg, position);
16364 //            Roo.log(this.el);
16365         }
16366         this.initEvents();
16367     },
16368     
16369     initEvents : function()
16370     {
16371         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16372         this.el.enableDisplayMode('block');
16373         this.el.hide();
16374         if (this.over === false) {
16375             return; 
16376         }
16377         if (this.triggers === false) {
16378             return;
16379         }
16380         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16381         var triggers = this.trigger ? this.trigger.split(' ') : [];
16382         Roo.each(triggers, function(trigger) {
16383         
16384             if (trigger == 'click') {
16385                 on_el.on('click', this.toggle, this);
16386             } else if (trigger != 'manual') {
16387                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16388                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16389       
16390                 on_el.on(eventIn  ,this.enter, this);
16391                 on_el.on(eventOut, this.leave, this);
16392             }
16393         }, this);
16394         
16395     },
16396     
16397     
16398     // private
16399     timeout : null,
16400     hoverState : null,
16401     
16402     toggle : function () {
16403         this.hoverState == 'in' ? this.leave() : this.enter();
16404     },
16405     
16406     enter : function () {
16407         
16408         clearTimeout(this.timeout);
16409     
16410         this.hoverState = 'in';
16411     
16412         if (!this.delay || !this.delay.show) {
16413             this.show();
16414             return;
16415         }
16416         var _t = this;
16417         this.timeout = setTimeout(function () {
16418             if (_t.hoverState == 'in') {
16419                 _t.show();
16420             }
16421         }, this.delay.show)
16422     },
16423     
16424     leave : function() {
16425         clearTimeout(this.timeout);
16426     
16427         this.hoverState = 'out';
16428     
16429         if (!this.delay || !this.delay.hide) {
16430             this.hide();
16431             return;
16432         }
16433         var _t = this;
16434         this.timeout = setTimeout(function () {
16435             if (_t.hoverState == 'out') {
16436                 _t.hide();
16437             }
16438         }, this.delay.hide)
16439     },
16440     
16441     show : function (on_el)
16442     {
16443         if (!on_el) {
16444             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16445         }
16446         
16447         // set content.
16448         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16449         if (this.html !== false) {
16450             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16451         }
16452         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16453         if (!this.title.length) {
16454             this.el.select('.popover-title',true).hide();
16455         }
16456         
16457         var placement = typeof this.placement == 'function' ?
16458             this.placement.call(this, this.el, on_el) :
16459             this.placement;
16460             
16461         var autoToken = /\s?auto?\s?/i;
16462         var autoPlace = autoToken.test(placement);
16463         if (autoPlace) {
16464             placement = placement.replace(autoToken, '') || 'top';
16465         }
16466         
16467         //this.el.detach()
16468         //this.el.setXY([0,0]);
16469         this.el.show();
16470         this.el.dom.style.display='block';
16471         this.el.addClass(placement);
16472         
16473         //this.el.appendTo(on_el);
16474         
16475         var p = this.getPosition();
16476         var box = this.el.getBox();
16477         
16478         if (autoPlace) {
16479             // fixme..
16480         }
16481         var align = Roo.bootstrap.Popover.alignment[placement];
16482         this.el.alignTo(on_el, align[0],align[1]);
16483         //var arrow = this.el.select('.arrow',true).first();
16484         //arrow.set(align[2], 
16485         
16486         this.el.addClass('in');
16487         
16488         
16489         if (this.el.hasClass('fade')) {
16490             // fade it?
16491         }
16492         
16493         this.hoverState = 'in';
16494         
16495         this.fireEvent('show', this);
16496         
16497     },
16498     hide : function()
16499     {
16500         this.el.setXY([0,0]);
16501         this.el.removeClass('in');
16502         this.el.hide();
16503         this.hoverState = null;
16504         
16505         this.fireEvent('hide', this);
16506     }
16507     
16508 });
16509
16510 Roo.bootstrap.Popover.alignment = {
16511     'left' : ['r-l', [-10,0], 'right'],
16512     'right' : ['l-r', [10,0], 'left'],
16513     'bottom' : ['t-b', [0,10], 'top'],
16514     'top' : [ 'b-t', [0,-10], 'bottom']
16515 };
16516
16517  /*
16518  * - LGPL
16519  *
16520  * Progress
16521  * 
16522  */
16523
16524 /**
16525  * @class Roo.bootstrap.Progress
16526  * @extends Roo.bootstrap.Component
16527  * Bootstrap Progress class
16528  * @cfg {Boolean} striped striped of the progress bar
16529  * @cfg {Boolean} active animated of the progress bar
16530  * 
16531  * 
16532  * @constructor
16533  * Create a new Progress
16534  * @param {Object} config The config object
16535  */
16536
16537 Roo.bootstrap.Progress = function(config){
16538     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16539 };
16540
16541 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16542     
16543     striped : false,
16544     active: false,
16545     
16546     getAutoCreate : function(){
16547         var cfg = {
16548             tag: 'div',
16549             cls: 'progress'
16550         };
16551         
16552         
16553         if(this.striped){
16554             cfg.cls += ' progress-striped';
16555         }
16556       
16557         if(this.active){
16558             cfg.cls += ' active';
16559         }
16560         
16561         
16562         return cfg;
16563     }
16564    
16565 });
16566
16567  
16568
16569  /*
16570  * - LGPL
16571  *
16572  * ProgressBar
16573  * 
16574  */
16575
16576 /**
16577  * @class Roo.bootstrap.ProgressBar
16578  * @extends Roo.bootstrap.Component
16579  * Bootstrap ProgressBar class
16580  * @cfg {Number} aria_valuenow aria-value now
16581  * @cfg {Number} aria_valuemin aria-value min
16582  * @cfg {Number} aria_valuemax aria-value max
16583  * @cfg {String} label label for the progress bar
16584  * @cfg {String} panel (success | info | warning | danger )
16585  * @cfg {String} role role of the progress bar
16586  * @cfg {String} sr_only text
16587  * 
16588  * 
16589  * @constructor
16590  * Create a new ProgressBar
16591  * @param {Object} config The config object
16592  */
16593
16594 Roo.bootstrap.ProgressBar = function(config){
16595     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16596 };
16597
16598 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16599     
16600     aria_valuenow : 0,
16601     aria_valuemin : 0,
16602     aria_valuemax : 100,
16603     label : false,
16604     panel : false,
16605     role : false,
16606     sr_only: false,
16607     
16608     getAutoCreate : function()
16609     {
16610         
16611         var cfg = {
16612             tag: 'div',
16613             cls: 'progress-bar',
16614             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16615         };
16616         
16617         if(this.sr_only){
16618             cfg.cn = {
16619                 tag: 'span',
16620                 cls: 'sr-only',
16621                 html: this.sr_only
16622             }
16623         }
16624         
16625         if(this.role){
16626             cfg.role = this.role;
16627         }
16628         
16629         if(this.aria_valuenow){
16630             cfg['aria-valuenow'] = this.aria_valuenow;
16631         }
16632         
16633         if(this.aria_valuemin){
16634             cfg['aria-valuemin'] = this.aria_valuemin;
16635         }
16636         
16637         if(this.aria_valuemax){
16638             cfg['aria-valuemax'] = this.aria_valuemax;
16639         }
16640         
16641         if(this.label && !this.sr_only){
16642             cfg.html = this.label;
16643         }
16644         
16645         if(this.panel){
16646             cfg.cls += ' progress-bar-' + this.panel;
16647         }
16648         
16649         return cfg;
16650     },
16651     
16652     update : function(aria_valuenow)
16653     {
16654         this.aria_valuenow = aria_valuenow;
16655         
16656         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16657     }
16658    
16659 });
16660
16661  
16662
16663  /*
16664  * - LGPL
16665  *
16666  * column
16667  * 
16668  */
16669
16670 /**
16671  * @class Roo.bootstrap.TabGroup
16672  * @extends Roo.bootstrap.Column
16673  * Bootstrap Column class
16674  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16675  * @cfg {Boolean} carousel true to make the group behave like a carousel
16676  * @cfg {Boolean} bullets show bullets for the panels
16677  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16678  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16679  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16680  * @cfg {Boolean} showarrow (true|false) show arrow default true
16681  * 
16682  * @constructor
16683  * Create a new TabGroup
16684  * @param {Object} config The config object
16685  */
16686
16687 Roo.bootstrap.TabGroup = function(config){
16688     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16689     if (!this.navId) {
16690         this.navId = Roo.id();
16691     }
16692     this.tabs = [];
16693     Roo.bootstrap.TabGroup.register(this);
16694     
16695 };
16696
16697 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16698     
16699     carousel : false,
16700     transition : false,
16701     bullets : 0,
16702     timer : 0,
16703     autoslide : false,
16704     slideFn : false,
16705     slideOnTouch : false,
16706     showarrow : true,
16707     
16708     getAutoCreate : function()
16709     {
16710         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16711         
16712         cfg.cls += ' tab-content';
16713         
16714         if (this.carousel) {
16715             cfg.cls += ' carousel slide';
16716             
16717             cfg.cn = [{
16718                cls : 'carousel-inner',
16719                cn : []
16720             }];
16721         
16722             if(this.bullets  && !Roo.isTouch){
16723                 
16724                 var bullets = {
16725                     cls : 'carousel-bullets',
16726                     cn : []
16727                 };
16728                
16729                 if(this.bullets_cls){
16730                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16731                 }
16732                 
16733                 bullets.cn.push({
16734                     cls : 'clear'
16735                 });
16736                 
16737                 cfg.cn[0].cn.push(bullets);
16738             }
16739             
16740             if(this.showarrow){
16741                 cfg.cn[0].cn.push({
16742                     tag : 'div',
16743                     class : 'carousel-arrow',
16744                     cn : [
16745                         {
16746                             tag : 'div',
16747                             class : 'carousel-prev',
16748                             cn : [
16749                                 {
16750                                     tag : 'i',
16751                                     class : 'fa fa-chevron-left'
16752                                 }
16753                             ]
16754                         },
16755                         {
16756                             tag : 'div',
16757                             class : 'carousel-next',
16758                             cn : [
16759                                 {
16760                                     tag : 'i',
16761                                     class : 'fa fa-chevron-right'
16762                                 }
16763                             ]
16764                         }
16765                     ]
16766                 });
16767             }
16768             
16769         }
16770         
16771         return cfg;
16772     },
16773     
16774     initEvents:  function()
16775     {
16776         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16777             this.el.on("touchstart", this.onTouchStart, this);
16778         }
16779         
16780         if(this.autoslide){
16781             var _this = this;
16782             
16783             this.slideFn = window.setInterval(function() {
16784                 _this.showPanelNext();
16785             }, this.timer);
16786         }
16787         
16788         if(this.showarrow){
16789             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16790             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16791         }
16792         
16793         
16794     },
16795     
16796     onTouchStart : function(e, el, o)
16797     {
16798         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16799             return;
16800         }
16801         
16802         this.showPanelNext();
16803     },
16804     
16805     getChildContainer : function()
16806     {
16807         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16808     },
16809     
16810     /**
16811     * register a Navigation item
16812     * @param {Roo.bootstrap.NavItem} the navitem to add
16813     */
16814     register : function(item)
16815     {
16816         this.tabs.push( item);
16817         item.navId = this.navId; // not really needed..
16818         this.addBullet();
16819     
16820     },
16821     
16822     getActivePanel : function()
16823     {
16824         var r = false;
16825         Roo.each(this.tabs, function(t) {
16826             if (t.active) {
16827                 r = t;
16828                 return false;
16829             }
16830             return null;
16831         });
16832         return r;
16833         
16834     },
16835     getPanelByName : function(n)
16836     {
16837         var r = false;
16838         Roo.each(this.tabs, function(t) {
16839             if (t.tabId == n) {
16840                 r = t;
16841                 return false;
16842             }
16843             return null;
16844         });
16845         return r;
16846     },
16847     indexOfPanel : function(p)
16848     {
16849         var r = false;
16850         Roo.each(this.tabs, function(t,i) {
16851             if (t.tabId == p.tabId) {
16852                 r = i;
16853                 return false;
16854             }
16855             return null;
16856         });
16857         return r;
16858     },
16859     /**
16860      * show a specific panel
16861      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16862      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16863      */
16864     showPanel : function (pan)
16865     {
16866         if(this.transition || typeof(pan) == 'undefined'){
16867             Roo.log("waiting for the transitionend");
16868             return;
16869         }
16870         
16871         if (typeof(pan) == 'number') {
16872             pan = this.tabs[pan];
16873         }
16874         
16875         if (typeof(pan) == 'string') {
16876             pan = this.getPanelByName(pan);
16877         }
16878         
16879         var cur = this.getActivePanel();
16880         
16881         if(!pan || !cur){
16882             Roo.log('pan or acitve pan is undefined');
16883             return false;
16884         }
16885         
16886         if (pan.tabId == this.getActivePanel().tabId) {
16887             return true;
16888         }
16889         
16890         if (false === cur.fireEvent('beforedeactivate')) {
16891             return false;
16892         }
16893         
16894         if(this.bullets > 0 && !Roo.isTouch){
16895             this.setActiveBullet(this.indexOfPanel(pan));
16896         }
16897         
16898         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16899             
16900             this.transition = true;
16901             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16902             var lr = dir == 'next' ? 'left' : 'right';
16903             pan.el.addClass(dir); // or prev
16904             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16905             cur.el.addClass(lr); // or right
16906             pan.el.addClass(lr);
16907             
16908             var _this = this;
16909             cur.el.on('transitionend', function() {
16910                 Roo.log("trans end?");
16911                 
16912                 pan.el.removeClass([lr,dir]);
16913                 pan.setActive(true);
16914                 
16915                 cur.el.removeClass([lr]);
16916                 cur.setActive(false);
16917                 
16918                 _this.transition = false;
16919                 
16920             }, this, { single:  true } );
16921             
16922             return true;
16923         }
16924         
16925         cur.setActive(false);
16926         pan.setActive(true);
16927         
16928         return true;
16929         
16930     },
16931     showPanelNext : function()
16932     {
16933         var i = this.indexOfPanel(this.getActivePanel());
16934         
16935         if (i >= this.tabs.length - 1 && !this.autoslide) {
16936             return;
16937         }
16938         
16939         if (i >= this.tabs.length - 1 && this.autoslide) {
16940             i = -1;
16941         }
16942         
16943         this.showPanel(this.tabs[i+1]);
16944     },
16945     
16946     showPanelPrev : function()
16947     {
16948         var i = this.indexOfPanel(this.getActivePanel());
16949         
16950         if (i  < 1 && !this.autoslide) {
16951             return;
16952         }
16953         
16954         if (i < 1 && this.autoslide) {
16955             i = this.tabs.length;
16956         }
16957         
16958         this.showPanel(this.tabs[i-1]);
16959     },
16960     
16961     
16962     addBullet: function()
16963     {
16964         if(!this.bullets || Roo.isTouch){
16965             return;
16966         }
16967         var ctr = this.el.select('.carousel-bullets',true).first();
16968         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16969         var bullet = ctr.createChild({
16970             cls : 'bullet bullet-' + i
16971         },ctr.dom.lastChild);
16972         
16973         
16974         var _this = this;
16975         
16976         bullet.on('click', (function(e, el, o, ii, t){
16977
16978             e.preventDefault();
16979
16980             this.showPanel(ii);
16981
16982             if(this.autoslide && this.slideFn){
16983                 clearInterval(this.slideFn);
16984                 this.slideFn = window.setInterval(function() {
16985                     _this.showPanelNext();
16986                 }, this.timer);
16987             }
16988
16989         }).createDelegate(this, [i, bullet], true));
16990                 
16991         
16992     },
16993      
16994     setActiveBullet : function(i)
16995     {
16996         if(Roo.isTouch){
16997             return;
16998         }
16999         
17000         Roo.each(this.el.select('.bullet', true).elements, function(el){
17001             el.removeClass('selected');
17002         });
17003
17004         var bullet = this.el.select('.bullet-' + i, true).first();
17005         
17006         if(!bullet){
17007             return;
17008         }
17009         
17010         bullet.addClass('selected');
17011     }
17012     
17013     
17014   
17015 });
17016
17017  
17018
17019  
17020  
17021 Roo.apply(Roo.bootstrap.TabGroup, {
17022     
17023     groups: {},
17024      /**
17025     * register a Navigation Group
17026     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17027     */
17028     register : function(navgrp)
17029     {
17030         this.groups[navgrp.navId] = navgrp;
17031         
17032     },
17033     /**
17034     * fetch a Navigation Group based on the navigation ID
17035     * if one does not exist , it will get created.
17036     * @param {string} the navgroup to add
17037     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17038     */
17039     get: function(navId) {
17040         if (typeof(this.groups[navId]) == 'undefined') {
17041             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17042         }
17043         return this.groups[navId] ;
17044     }
17045     
17046     
17047     
17048 });
17049
17050  /*
17051  * - LGPL
17052  *
17053  * TabPanel
17054  * 
17055  */
17056
17057 /**
17058  * @class Roo.bootstrap.TabPanel
17059  * @extends Roo.bootstrap.Component
17060  * Bootstrap TabPanel class
17061  * @cfg {Boolean} active panel active
17062  * @cfg {String} html panel content
17063  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17064  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17065  * @cfg {String} href click to link..
17066  * 
17067  * 
17068  * @constructor
17069  * Create a new TabPanel
17070  * @param {Object} config The config object
17071  */
17072
17073 Roo.bootstrap.TabPanel = function(config){
17074     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17075     this.addEvents({
17076         /**
17077              * @event changed
17078              * Fires when the active status changes
17079              * @param {Roo.bootstrap.TabPanel} this
17080              * @param {Boolean} state the new state
17081             
17082          */
17083         'changed': true,
17084         /**
17085              * @event beforedeactivate
17086              * Fires before a tab is de-activated - can be used to do validation on a form.
17087              * @param {Roo.bootstrap.TabPanel} this
17088              * @return {Boolean} false if there is an error
17089             
17090          */
17091         'beforedeactivate': true
17092      });
17093     
17094     this.tabId = this.tabId || Roo.id();
17095   
17096 };
17097
17098 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17099     
17100     active: false,
17101     html: false,
17102     tabId: false,
17103     navId : false,
17104     href : '',
17105     
17106     getAutoCreate : function(){
17107         var cfg = {
17108             tag: 'div',
17109             // item is needed for carousel - not sure if it has any effect otherwise
17110             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17111             html: this.html || ''
17112         };
17113         
17114         if(this.active){
17115             cfg.cls += ' active';
17116         }
17117         
17118         if(this.tabId){
17119             cfg.tabId = this.tabId;
17120         }
17121         
17122         
17123         return cfg;
17124     },
17125     
17126     initEvents:  function()
17127     {
17128         var p = this.parent();
17129         this.navId = this.navId || p.navId;
17130         
17131         if (typeof(this.navId) != 'undefined') {
17132             // not really needed.. but just in case.. parent should be a NavGroup.
17133             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17134             
17135             tg.register(this);
17136             
17137             var i = tg.tabs.length - 1;
17138             
17139             if(this.active && tg.bullets > 0 && i < tg.bullets){
17140                 tg.setActiveBullet(i);
17141             }
17142         }
17143         
17144         if(this.href.length){
17145             this.el.on('click', this.onClick, this);
17146         }
17147         
17148     },
17149     
17150     onRender : function(ct, position)
17151     {
17152        // Roo.log("Call onRender: " + this.xtype);
17153         
17154         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17155         
17156         
17157         
17158         
17159         
17160     },
17161     
17162     setActive: function(state)
17163     {
17164         Roo.log("panel - set active " + this.tabId + "=" + state);
17165         
17166         this.active = state;
17167         if (!state) {
17168             this.el.removeClass('active');
17169             
17170         } else  if (!this.el.hasClass('active')) {
17171             this.el.addClass('active');
17172         }
17173         
17174         this.fireEvent('changed', this, state);
17175     },
17176     
17177     onClick: function(e)
17178     {
17179         e.preventDefault();
17180         
17181         window.location.href = this.href;
17182     }
17183     
17184     
17185 });
17186  
17187
17188  
17189
17190  /*
17191  * - LGPL
17192  *
17193  * DateField
17194  * 
17195  */
17196
17197 /**
17198  * @class Roo.bootstrap.DateField
17199  * @extends Roo.bootstrap.Input
17200  * Bootstrap DateField class
17201  * @cfg {Number} weekStart default 0
17202  * @cfg {String} viewMode default empty, (months|years)
17203  * @cfg {String} minViewMode default empty, (months|years)
17204  * @cfg {Number} startDate default -Infinity
17205  * @cfg {Number} endDate default Infinity
17206  * @cfg {Boolean} todayHighlight default false
17207  * @cfg {Boolean} todayBtn default false
17208  * @cfg {Boolean} calendarWeeks default false
17209  * @cfg {Object} daysOfWeekDisabled default empty
17210  * @cfg {Boolean} singleMode default false (true | false)
17211  * 
17212  * @cfg {Boolean} keyboardNavigation default true
17213  * @cfg {String} language default en
17214  * 
17215  * @constructor
17216  * Create a new DateField
17217  * @param {Object} config The config object
17218  */
17219
17220 Roo.bootstrap.DateField = function(config){
17221     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17222      this.addEvents({
17223             /**
17224              * @event show
17225              * Fires when this field show.
17226              * @param {Roo.bootstrap.DateField} this
17227              * @param {Mixed} date The date value
17228              */
17229             show : true,
17230             /**
17231              * @event show
17232              * Fires when this field hide.
17233              * @param {Roo.bootstrap.DateField} this
17234              * @param {Mixed} date The date value
17235              */
17236             hide : true,
17237             /**
17238              * @event select
17239              * Fires when select a date.
17240              * @param {Roo.bootstrap.DateField} this
17241              * @param {Mixed} date The date value
17242              */
17243             select : true,
17244             /**
17245              * @event beforeselect
17246              * Fires when before select a date.
17247              * @param {Roo.bootstrap.DateField} this
17248              * @param {Mixed} date The date value
17249              */
17250             beforeselect : true
17251         });
17252 };
17253
17254 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17255     
17256     /**
17257      * @cfg {String} format
17258      * The default date format string which can be overriden for localization support.  The format must be
17259      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17260      */
17261     format : "m/d/y",
17262     /**
17263      * @cfg {String} altFormats
17264      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17265      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17266      */
17267     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17268     
17269     weekStart : 0,
17270     
17271     viewMode : '',
17272     
17273     minViewMode : '',
17274     
17275     todayHighlight : false,
17276     
17277     todayBtn: false,
17278     
17279     language: 'en',
17280     
17281     keyboardNavigation: true,
17282     
17283     calendarWeeks: false,
17284     
17285     startDate: -Infinity,
17286     
17287     endDate: Infinity,
17288     
17289     daysOfWeekDisabled: [],
17290     
17291     _events: [],
17292     
17293     singleMode : false,
17294     
17295     UTCDate: function()
17296     {
17297         return new Date(Date.UTC.apply(Date, arguments));
17298     },
17299     
17300     UTCToday: function()
17301     {
17302         var today = new Date();
17303         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17304     },
17305     
17306     getDate: function() {
17307             var d = this.getUTCDate();
17308             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17309     },
17310     
17311     getUTCDate: function() {
17312             return this.date;
17313     },
17314     
17315     setDate: function(d) {
17316             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17317     },
17318     
17319     setUTCDate: function(d) {
17320             this.date = d;
17321             this.setValue(this.formatDate(this.date));
17322     },
17323         
17324     onRender: function(ct, position)
17325     {
17326         
17327         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17328         
17329         this.language = this.language || 'en';
17330         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17331         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17332         
17333         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17334         this.format = this.format || 'm/d/y';
17335         this.isInline = false;
17336         this.isInput = true;
17337         this.component = this.el.select('.add-on', true).first() || false;
17338         this.component = (this.component && this.component.length === 0) ? false : this.component;
17339         this.hasInput = this.component && this.inputEl().length;
17340         
17341         if (typeof(this.minViewMode === 'string')) {
17342             switch (this.minViewMode) {
17343                 case 'months':
17344                     this.minViewMode = 1;
17345                     break;
17346                 case 'years':
17347                     this.minViewMode = 2;
17348                     break;
17349                 default:
17350                     this.minViewMode = 0;
17351                     break;
17352             }
17353         }
17354         
17355         if (typeof(this.viewMode === 'string')) {
17356             switch (this.viewMode) {
17357                 case 'months':
17358                     this.viewMode = 1;
17359                     break;
17360                 case 'years':
17361                     this.viewMode = 2;
17362                     break;
17363                 default:
17364                     this.viewMode = 0;
17365                     break;
17366             }
17367         }
17368                 
17369         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17370         
17371 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17372         
17373         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17374         
17375         this.picker().on('mousedown', this.onMousedown, this);
17376         this.picker().on('click', this.onClick, this);
17377         
17378         this.picker().addClass('datepicker-dropdown');
17379         
17380         this.startViewMode = this.viewMode;
17381         
17382         if(this.singleMode){
17383             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17384                 v.setVisibilityMode(Roo.Element.DISPLAY);
17385                 v.hide();
17386             });
17387             
17388             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17389                 v.setStyle('width', '189px');
17390             });
17391         }
17392         
17393         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17394             if(!this.calendarWeeks){
17395                 v.remove();
17396                 return;
17397             }
17398             
17399             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17400             v.attr('colspan', function(i, val){
17401                 return parseInt(val) + 1;
17402             });
17403         });
17404                         
17405         
17406         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17407         
17408         this.setStartDate(this.startDate);
17409         this.setEndDate(this.endDate);
17410         
17411         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17412         
17413         this.fillDow();
17414         this.fillMonths();
17415         this.update();
17416         this.showMode();
17417         
17418         if(this.isInline) {
17419             this.show();
17420         }
17421     },
17422     
17423     picker : function()
17424     {
17425         return this.pickerEl;
17426 //        return this.el.select('.datepicker', true).first();
17427     },
17428     
17429     fillDow: function()
17430     {
17431         var dowCnt = this.weekStart;
17432         
17433         var dow = {
17434             tag: 'tr',
17435             cn: [
17436                 
17437             ]
17438         };
17439         
17440         if(this.calendarWeeks){
17441             dow.cn.push({
17442                 tag: 'th',
17443                 cls: 'cw',
17444                 html: '&nbsp;'
17445             })
17446         }
17447         
17448         while (dowCnt < this.weekStart + 7) {
17449             dow.cn.push({
17450                 tag: 'th',
17451                 cls: 'dow',
17452                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17453             });
17454         }
17455         
17456         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17457     },
17458     
17459     fillMonths: function()
17460     {    
17461         var i = 0;
17462         var months = this.picker().select('>.datepicker-months td', true).first();
17463         
17464         months.dom.innerHTML = '';
17465         
17466         while (i < 12) {
17467             var month = {
17468                 tag: 'span',
17469                 cls: 'month',
17470                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17471             };
17472             
17473             months.createChild(month);
17474         }
17475         
17476     },
17477     
17478     update: function()
17479     {
17480         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
17481         
17482         if (this.date < this.startDate) {
17483             this.viewDate = new Date(this.startDate);
17484         } else if (this.date > this.endDate) {
17485             this.viewDate = new Date(this.endDate);
17486         } else {
17487             this.viewDate = new Date(this.date);
17488         }
17489         
17490         this.fill();
17491     },
17492     
17493     fill: function() 
17494     {
17495         var d = new Date(this.viewDate),
17496                 year = d.getUTCFullYear(),
17497                 month = d.getUTCMonth(),
17498                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17499                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17500                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17501                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17502                 currentDate = this.date && this.date.valueOf(),
17503                 today = this.UTCToday();
17504         
17505         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17506         
17507 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17508         
17509 //        this.picker.select('>tfoot th.today').
17510 //                                              .text(dates[this.language].today)
17511 //                                              .toggle(this.todayBtn !== false);
17512     
17513         this.updateNavArrows();
17514         this.fillMonths();
17515                                                 
17516         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17517         
17518         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17519          
17520         prevMonth.setUTCDate(day);
17521         
17522         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17523         
17524         var nextMonth = new Date(prevMonth);
17525         
17526         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17527         
17528         nextMonth = nextMonth.valueOf();
17529         
17530         var fillMonths = false;
17531         
17532         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17533         
17534         while(prevMonth.valueOf() < nextMonth) {
17535             var clsName = '';
17536             
17537             if (prevMonth.getUTCDay() === this.weekStart) {
17538                 if(fillMonths){
17539                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17540                 }
17541                     
17542                 fillMonths = {
17543                     tag: 'tr',
17544                     cn: []
17545                 };
17546                 
17547                 if(this.calendarWeeks){
17548                     // ISO 8601: First week contains first thursday.
17549                     // ISO also states week starts on Monday, but we can be more abstract here.
17550                     var
17551                     // Start of current week: based on weekstart/current date
17552                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17553                     // Thursday of this week
17554                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17555                     // First Thursday of year, year from thursday
17556                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17557                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17558                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17559                     
17560                     fillMonths.cn.push({
17561                         tag: 'td',
17562                         cls: 'cw',
17563                         html: calWeek
17564                     });
17565                 }
17566             }
17567             
17568             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17569                 clsName += ' old';
17570             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17571                 clsName += ' new';
17572             }
17573             if (this.todayHighlight &&
17574                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17575                 prevMonth.getUTCMonth() == today.getMonth() &&
17576                 prevMonth.getUTCDate() == today.getDate()) {
17577                 clsName += ' today';
17578             }
17579             
17580             if (currentDate && prevMonth.valueOf() === currentDate) {
17581                 clsName += ' active';
17582             }
17583             
17584             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17585                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17586                     clsName += ' disabled';
17587             }
17588             
17589             fillMonths.cn.push({
17590                 tag: 'td',
17591                 cls: 'day ' + clsName,
17592                 html: prevMonth.getDate()
17593             });
17594             
17595             prevMonth.setDate(prevMonth.getDate()+1);
17596         }
17597           
17598         var currentYear = this.date && this.date.getUTCFullYear();
17599         var currentMonth = this.date && this.date.getUTCMonth();
17600         
17601         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17602         
17603         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17604             v.removeClass('active');
17605             
17606             if(currentYear === year && k === currentMonth){
17607                 v.addClass('active');
17608             }
17609             
17610             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17611                 v.addClass('disabled');
17612             }
17613             
17614         });
17615         
17616         
17617         year = parseInt(year/10, 10) * 10;
17618         
17619         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17620         
17621         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17622         
17623         year -= 1;
17624         for (var i = -1; i < 11; i++) {
17625             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17626                 tag: 'span',
17627                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17628                 html: year
17629             });
17630             
17631             year += 1;
17632         }
17633     },
17634     
17635     showMode: function(dir) 
17636     {
17637         if (dir) {
17638             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17639         }
17640         
17641         Roo.each(this.picker().select('>div',true).elements, function(v){
17642             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17643             v.hide();
17644         });
17645         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17646     },
17647     
17648     place: function()
17649     {
17650         if(this.isInline) {
17651             return;
17652         }
17653         
17654         this.picker().removeClass(['bottom', 'top']);
17655         
17656         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17657             /*
17658              * place to the top of element!
17659              *
17660              */
17661             
17662             this.picker().addClass('top');
17663             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17664             
17665             return;
17666         }
17667         
17668         this.picker().addClass('bottom');
17669         
17670         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17671     },
17672     
17673     parseDate : function(value)
17674     {
17675         if(!value || value instanceof Date){
17676             return value;
17677         }
17678         var v = Date.parseDate(value, this.format);
17679         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17680             v = Date.parseDate(value, 'Y-m-d');
17681         }
17682         if(!v && this.altFormats){
17683             if(!this.altFormatsArray){
17684                 this.altFormatsArray = this.altFormats.split("|");
17685             }
17686             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17687                 v = Date.parseDate(value, this.altFormatsArray[i]);
17688             }
17689         }
17690         return v;
17691     },
17692     
17693     formatDate : function(date, fmt)
17694     {   
17695         return (!date || !(date instanceof Date)) ?
17696         date : date.dateFormat(fmt || this.format);
17697     },
17698     
17699     onFocus : function()
17700     {
17701         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17702         this.show();
17703     },
17704     
17705     onBlur : function()
17706     {
17707         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17708         
17709         var d = this.inputEl().getValue();
17710         
17711         this.setValue(d);
17712                 
17713         this.hide();
17714     },
17715     
17716     show : function()
17717     {
17718         this.picker().show();
17719         this.update();
17720         this.place();
17721         
17722         this.fireEvent('show', this, this.date);
17723     },
17724     
17725     hide : function()
17726     {
17727         if(this.isInline) {
17728             return;
17729         }
17730         this.picker().hide();
17731         this.viewMode = this.startViewMode;
17732         this.showMode();
17733         
17734         this.fireEvent('hide', this, this.date);
17735         
17736     },
17737     
17738     onMousedown: function(e)
17739     {
17740         e.stopPropagation();
17741         e.preventDefault();
17742     },
17743     
17744     keyup: function(e)
17745     {
17746         Roo.bootstrap.DateField.superclass.keyup.call(this);
17747         this.update();
17748     },
17749
17750     setValue: function(v)
17751     {
17752         if(this.fireEvent('beforeselect', this, v) !== false){
17753             var d = new Date(this.parseDate(v) ).clearTime();
17754         
17755             if(isNaN(d.getTime())){
17756                 this.date = this.viewDate = '';
17757                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17758                 return;
17759             }
17760
17761             v = this.formatDate(d);
17762
17763             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17764
17765             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17766
17767             this.update();
17768
17769             this.fireEvent('select', this, this.date);
17770         }
17771     },
17772     
17773     getValue: function()
17774     {
17775         return this.formatDate(this.date);
17776     },
17777     
17778     fireKey: function(e)
17779     {
17780         if (!this.picker().isVisible()){
17781             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17782                 this.show();
17783             }
17784             return;
17785         }
17786         
17787         var dateChanged = false,
17788         dir, day, month,
17789         newDate, newViewDate;
17790         
17791         switch(e.keyCode){
17792             case 27: // escape
17793                 this.hide();
17794                 e.preventDefault();
17795                 break;
17796             case 37: // left
17797             case 39: // right
17798                 if (!this.keyboardNavigation) {
17799                     break;
17800                 }
17801                 dir = e.keyCode == 37 ? -1 : 1;
17802                 
17803                 if (e.ctrlKey){
17804                     newDate = this.moveYear(this.date, dir);
17805                     newViewDate = this.moveYear(this.viewDate, dir);
17806                 } else if (e.shiftKey){
17807                     newDate = this.moveMonth(this.date, dir);
17808                     newViewDate = this.moveMonth(this.viewDate, dir);
17809                 } else {
17810                     newDate = new Date(this.date);
17811                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17812                     newViewDate = new Date(this.viewDate);
17813                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17814                 }
17815                 if (this.dateWithinRange(newDate)){
17816                     this.date = newDate;
17817                     this.viewDate = newViewDate;
17818                     this.setValue(this.formatDate(this.date));
17819 //                    this.update();
17820                     e.preventDefault();
17821                     dateChanged = true;
17822                 }
17823                 break;
17824             case 38: // up
17825             case 40: // down
17826                 if (!this.keyboardNavigation) {
17827                     break;
17828                 }
17829                 dir = e.keyCode == 38 ? -1 : 1;
17830                 if (e.ctrlKey){
17831                     newDate = this.moveYear(this.date, dir);
17832                     newViewDate = this.moveYear(this.viewDate, dir);
17833                 } else if (e.shiftKey){
17834                     newDate = this.moveMonth(this.date, dir);
17835                     newViewDate = this.moveMonth(this.viewDate, dir);
17836                 } else {
17837                     newDate = new Date(this.date);
17838                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17839                     newViewDate = new Date(this.viewDate);
17840                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17841                 }
17842                 if (this.dateWithinRange(newDate)){
17843                     this.date = newDate;
17844                     this.viewDate = newViewDate;
17845                     this.setValue(this.formatDate(this.date));
17846 //                    this.update();
17847                     e.preventDefault();
17848                     dateChanged = true;
17849                 }
17850                 break;
17851             case 13: // enter
17852                 this.setValue(this.formatDate(this.date));
17853                 this.hide();
17854                 e.preventDefault();
17855                 break;
17856             case 9: // tab
17857                 this.setValue(this.formatDate(this.date));
17858                 this.hide();
17859                 break;
17860             case 16: // shift
17861             case 17: // ctrl
17862             case 18: // alt
17863                 break;
17864             default :
17865                 this.hide();
17866                 
17867         }
17868     },
17869     
17870     
17871     onClick: function(e) 
17872     {
17873         e.stopPropagation();
17874         e.preventDefault();
17875         
17876         var target = e.getTarget();
17877         
17878         if(target.nodeName.toLowerCase() === 'i'){
17879             target = Roo.get(target).dom.parentNode;
17880         }
17881         
17882         var nodeName = target.nodeName;
17883         var className = target.className;
17884         var html = target.innerHTML;
17885         //Roo.log(nodeName);
17886         
17887         switch(nodeName.toLowerCase()) {
17888             case 'th':
17889                 switch(className) {
17890                     case 'switch':
17891                         this.showMode(1);
17892                         break;
17893                     case 'prev':
17894                     case 'next':
17895                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17896                         switch(this.viewMode){
17897                                 case 0:
17898                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17899                                         break;
17900                                 case 1:
17901                                 case 2:
17902                                         this.viewDate = this.moveYear(this.viewDate, dir);
17903                                         break;
17904                         }
17905                         this.fill();
17906                         break;
17907                     case 'today':
17908                         var date = new Date();
17909                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17910 //                        this.fill()
17911                         this.setValue(this.formatDate(this.date));
17912                         
17913                         this.hide();
17914                         break;
17915                 }
17916                 break;
17917             case 'span':
17918                 if (className.indexOf('disabled') < 0) {
17919                     this.viewDate.setUTCDate(1);
17920                     if (className.indexOf('month') > -1) {
17921                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17922                     } else {
17923                         var year = parseInt(html, 10) || 0;
17924                         this.viewDate.setUTCFullYear(year);
17925                         
17926                     }
17927                     
17928                     if(this.singleMode){
17929                         this.setValue(this.formatDate(this.viewDate));
17930                         this.hide();
17931                         return;
17932                     }
17933                     
17934                     this.showMode(-1);
17935                     this.fill();
17936                 }
17937                 break;
17938                 
17939             case 'td':
17940                 //Roo.log(className);
17941                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17942                     var day = parseInt(html, 10) || 1;
17943                     var year = this.viewDate.getUTCFullYear(),
17944                         month = this.viewDate.getUTCMonth();
17945
17946                     if (className.indexOf('old') > -1) {
17947                         if(month === 0 ){
17948                             month = 11;
17949                             year -= 1;
17950                         }else{
17951                             month -= 1;
17952                         }
17953                     } else if (className.indexOf('new') > -1) {
17954                         if (month == 11) {
17955                             month = 0;
17956                             year += 1;
17957                         } else {
17958                             month += 1;
17959                         }
17960                     }
17961                     //Roo.log([year,month,day]);
17962                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17963                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17964 //                    this.fill();
17965                     //Roo.log(this.formatDate(this.date));
17966                     this.setValue(this.formatDate(this.date));
17967                     this.hide();
17968                 }
17969                 break;
17970         }
17971     },
17972     
17973     setStartDate: function(startDate)
17974     {
17975         this.startDate = startDate || -Infinity;
17976         if (this.startDate !== -Infinity) {
17977             this.startDate = this.parseDate(this.startDate);
17978         }
17979         this.update();
17980         this.updateNavArrows();
17981     },
17982
17983     setEndDate: function(endDate)
17984     {
17985         this.endDate = endDate || Infinity;
17986         if (this.endDate !== Infinity) {
17987             this.endDate = this.parseDate(this.endDate);
17988         }
17989         this.update();
17990         this.updateNavArrows();
17991     },
17992     
17993     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17994     {
17995         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17996         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17997             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17998         }
17999         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18000             return parseInt(d, 10);
18001         });
18002         this.update();
18003         this.updateNavArrows();
18004     },
18005     
18006     updateNavArrows: function() 
18007     {
18008         if(this.singleMode){
18009             return;
18010         }
18011         
18012         var d = new Date(this.viewDate),
18013         year = d.getUTCFullYear(),
18014         month = d.getUTCMonth();
18015         
18016         Roo.each(this.picker().select('.prev', true).elements, function(v){
18017             v.show();
18018             switch (this.viewMode) {
18019                 case 0:
18020
18021                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18022                         v.hide();
18023                     }
18024                     break;
18025                 case 1:
18026                 case 2:
18027                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18028                         v.hide();
18029                     }
18030                     break;
18031             }
18032         });
18033         
18034         Roo.each(this.picker().select('.next', true).elements, function(v){
18035             v.show();
18036             switch (this.viewMode) {
18037                 case 0:
18038
18039                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18040                         v.hide();
18041                     }
18042                     break;
18043                 case 1:
18044                 case 2:
18045                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18046                         v.hide();
18047                     }
18048                     break;
18049             }
18050         })
18051     },
18052     
18053     moveMonth: function(date, dir)
18054     {
18055         if (!dir) {
18056             return date;
18057         }
18058         var new_date = new Date(date.valueOf()),
18059         day = new_date.getUTCDate(),
18060         month = new_date.getUTCMonth(),
18061         mag = Math.abs(dir),
18062         new_month, test;
18063         dir = dir > 0 ? 1 : -1;
18064         if (mag == 1){
18065             test = dir == -1
18066             // If going back one month, make sure month is not current month
18067             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18068             ? function(){
18069                 return new_date.getUTCMonth() == month;
18070             }
18071             // If going forward one month, make sure month is as expected
18072             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18073             : function(){
18074                 return new_date.getUTCMonth() != new_month;
18075             };
18076             new_month = month + dir;
18077             new_date.setUTCMonth(new_month);
18078             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18079             if (new_month < 0 || new_month > 11) {
18080                 new_month = (new_month + 12) % 12;
18081             }
18082         } else {
18083             // For magnitudes >1, move one month at a time...
18084             for (var i=0; i<mag; i++) {
18085                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18086                 new_date = this.moveMonth(new_date, dir);
18087             }
18088             // ...then reset the day, keeping it in the new month
18089             new_month = new_date.getUTCMonth();
18090             new_date.setUTCDate(day);
18091             test = function(){
18092                 return new_month != new_date.getUTCMonth();
18093             };
18094         }
18095         // Common date-resetting loop -- if date is beyond end of month, make it
18096         // end of month
18097         while (test()){
18098             new_date.setUTCDate(--day);
18099             new_date.setUTCMonth(new_month);
18100         }
18101         return new_date;
18102     },
18103
18104     moveYear: function(date, dir)
18105     {
18106         return this.moveMonth(date, dir*12);
18107     },
18108
18109     dateWithinRange: function(date)
18110     {
18111         return date >= this.startDate && date <= this.endDate;
18112     },
18113
18114     
18115     remove: function() 
18116     {
18117         this.picker().remove();
18118     },
18119     
18120     validateValue : function(value)
18121     {
18122         if(value.length < 1)  {
18123             if(this.allowBlank){
18124                 return true;
18125             }
18126             return false;
18127         }
18128         
18129         if(value.length < this.minLength){
18130             return false;
18131         }
18132         if(value.length > this.maxLength){
18133             return false;
18134         }
18135         if(this.vtype){
18136             var vt = Roo.form.VTypes;
18137             if(!vt[this.vtype](value, this)){
18138                 return false;
18139             }
18140         }
18141         if(typeof this.validator == "function"){
18142             var msg = this.validator(value);
18143             if(msg !== true){
18144                 return false;
18145             }
18146         }
18147         
18148         if(this.regex && !this.regex.test(value)){
18149             return false;
18150         }
18151         
18152         if(typeof(this.parseDate(value)) == 'undefined'){
18153             return false;
18154         }
18155         
18156         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18157             return false;
18158         }      
18159         
18160         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18161             return false;
18162         } 
18163         
18164         
18165         return true;
18166     }
18167    
18168 });
18169
18170 Roo.apply(Roo.bootstrap.DateField,  {
18171     
18172     head : {
18173         tag: 'thead',
18174         cn: [
18175         {
18176             tag: 'tr',
18177             cn: [
18178             {
18179                 tag: 'th',
18180                 cls: 'prev',
18181                 html: '<i class="fa fa-arrow-left"/>'
18182             },
18183             {
18184                 tag: 'th',
18185                 cls: 'switch',
18186                 colspan: '5'
18187             },
18188             {
18189                 tag: 'th',
18190                 cls: 'next',
18191                 html: '<i class="fa fa-arrow-right"/>'
18192             }
18193
18194             ]
18195         }
18196         ]
18197     },
18198     
18199     content : {
18200         tag: 'tbody',
18201         cn: [
18202         {
18203             tag: 'tr',
18204             cn: [
18205             {
18206                 tag: 'td',
18207                 colspan: '7'
18208             }
18209             ]
18210         }
18211         ]
18212     },
18213     
18214     footer : {
18215         tag: 'tfoot',
18216         cn: [
18217         {
18218             tag: 'tr',
18219             cn: [
18220             {
18221                 tag: 'th',
18222                 colspan: '7',
18223                 cls: 'today'
18224             }
18225                     
18226             ]
18227         }
18228         ]
18229     },
18230     
18231     dates:{
18232         en: {
18233             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18234             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18235             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18236             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18237             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18238             today: "Today"
18239         }
18240     },
18241     
18242     modes: [
18243     {
18244         clsName: 'days',
18245         navFnc: 'Month',
18246         navStep: 1
18247     },
18248     {
18249         clsName: 'months',
18250         navFnc: 'FullYear',
18251         navStep: 1
18252     },
18253     {
18254         clsName: 'years',
18255         navFnc: 'FullYear',
18256         navStep: 10
18257     }]
18258 });
18259
18260 Roo.apply(Roo.bootstrap.DateField,  {
18261   
18262     template : {
18263         tag: 'div',
18264         cls: 'datepicker dropdown-menu roo-dynamic',
18265         cn: [
18266         {
18267             tag: 'div',
18268             cls: 'datepicker-days',
18269             cn: [
18270             {
18271                 tag: 'table',
18272                 cls: 'table-condensed',
18273                 cn:[
18274                 Roo.bootstrap.DateField.head,
18275                 {
18276                     tag: 'tbody'
18277                 },
18278                 Roo.bootstrap.DateField.footer
18279                 ]
18280             }
18281             ]
18282         },
18283         {
18284             tag: 'div',
18285             cls: 'datepicker-months',
18286             cn: [
18287             {
18288                 tag: 'table',
18289                 cls: 'table-condensed',
18290                 cn:[
18291                 Roo.bootstrap.DateField.head,
18292                 Roo.bootstrap.DateField.content,
18293                 Roo.bootstrap.DateField.footer
18294                 ]
18295             }
18296             ]
18297         },
18298         {
18299             tag: 'div',
18300             cls: 'datepicker-years',
18301             cn: [
18302             {
18303                 tag: 'table',
18304                 cls: 'table-condensed',
18305                 cn:[
18306                 Roo.bootstrap.DateField.head,
18307                 Roo.bootstrap.DateField.content,
18308                 Roo.bootstrap.DateField.footer
18309                 ]
18310             }
18311             ]
18312         }
18313         ]
18314     }
18315 });
18316
18317  
18318
18319  /*
18320  * - LGPL
18321  *
18322  * TimeField
18323  * 
18324  */
18325
18326 /**
18327  * @class Roo.bootstrap.TimeField
18328  * @extends Roo.bootstrap.Input
18329  * Bootstrap DateField class
18330  * 
18331  * 
18332  * @constructor
18333  * Create a new TimeField
18334  * @param {Object} config The config object
18335  */
18336
18337 Roo.bootstrap.TimeField = function(config){
18338     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18339     this.addEvents({
18340             /**
18341              * @event show
18342              * Fires when this field show.
18343              * @param {Roo.bootstrap.DateField} thisthis
18344              * @param {Mixed} date The date value
18345              */
18346             show : true,
18347             /**
18348              * @event show
18349              * Fires when this field hide.
18350              * @param {Roo.bootstrap.DateField} this
18351              * @param {Mixed} date The date value
18352              */
18353             hide : true,
18354             /**
18355              * @event select
18356              * Fires when select a date.
18357              * @param {Roo.bootstrap.DateField} this
18358              * @param {Mixed} date The date value
18359              */
18360             select : true
18361         });
18362 };
18363
18364 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18365     
18366     /**
18367      * @cfg {String} format
18368      * The default time format string which can be overriden for localization support.  The format must be
18369      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18370      */
18371     format : "H:i",
18372        
18373     onRender: function(ct, position)
18374     {
18375         
18376         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18377                 
18378         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18379         
18380         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18381         
18382         this.pop = this.picker().select('>.datepicker-time',true).first();
18383         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18384         
18385         this.picker().on('mousedown', this.onMousedown, this);
18386         this.picker().on('click', this.onClick, this);
18387         
18388         this.picker().addClass('datepicker-dropdown');
18389     
18390         this.fillTime();
18391         this.update();
18392             
18393         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18394         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18395         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18396         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18397         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18398         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18399
18400     },
18401     
18402     fireKey: function(e){
18403         if (!this.picker().isVisible()){
18404             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18405                 this.show();
18406             }
18407             return;
18408         }
18409
18410         e.preventDefault();
18411         
18412         switch(e.keyCode){
18413             case 27: // escape
18414                 this.hide();
18415                 break;
18416             case 37: // left
18417             case 39: // right
18418                 this.onTogglePeriod();
18419                 break;
18420             case 38: // up
18421                 this.onIncrementMinutes();
18422                 break;
18423             case 40: // down
18424                 this.onDecrementMinutes();
18425                 break;
18426             case 13: // enter
18427             case 9: // tab
18428                 this.setTime();
18429                 break;
18430         }
18431     },
18432     
18433     onClick: function(e) {
18434         e.stopPropagation();
18435         e.preventDefault();
18436     },
18437     
18438     picker : function()
18439     {
18440         return this.el.select('.datepicker', true).first();
18441     },
18442     
18443     fillTime: function()
18444     {    
18445         var time = this.pop.select('tbody', true).first();
18446         
18447         time.dom.innerHTML = '';
18448         
18449         time.createChild({
18450             tag: 'tr',
18451             cn: [
18452                 {
18453                     tag: 'td',
18454                     cn: [
18455                         {
18456                             tag: 'a',
18457                             href: '#',
18458                             cls: 'btn',
18459                             cn: [
18460                                 {
18461                                     tag: 'span',
18462                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18463                                 }
18464                             ]
18465                         } 
18466                     ]
18467                 },
18468                 {
18469                     tag: 'td',
18470                     cls: 'separator'
18471                 },
18472                 {
18473                     tag: 'td',
18474                     cn: [
18475                         {
18476                             tag: 'a',
18477                             href: '#',
18478                             cls: 'btn',
18479                             cn: [
18480                                 {
18481                                     tag: 'span',
18482                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18483                                 }
18484                             ]
18485                         }
18486                     ]
18487                 },
18488                 {
18489                     tag: 'td',
18490                     cls: 'separator'
18491                 }
18492             ]
18493         });
18494         
18495         time.createChild({
18496             tag: 'tr',
18497             cn: [
18498                 {
18499                     tag: 'td',
18500                     cn: [
18501                         {
18502                             tag: 'span',
18503                             cls: 'timepicker-hour',
18504                             html: '00'
18505                         }  
18506                     ]
18507                 },
18508                 {
18509                     tag: 'td',
18510                     cls: 'separator',
18511                     html: ':'
18512                 },
18513                 {
18514                     tag: 'td',
18515                     cn: [
18516                         {
18517                             tag: 'span',
18518                             cls: 'timepicker-minute',
18519                             html: '00'
18520                         }  
18521                     ]
18522                 },
18523                 {
18524                     tag: 'td',
18525                     cls: 'separator'
18526                 },
18527                 {
18528                     tag: 'td',
18529                     cn: [
18530                         {
18531                             tag: 'button',
18532                             type: 'button',
18533                             cls: 'btn btn-primary period',
18534                             html: 'AM'
18535                             
18536                         }
18537                     ]
18538                 }
18539             ]
18540         });
18541         
18542         time.createChild({
18543             tag: 'tr',
18544             cn: [
18545                 {
18546                     tag: 'td',
18547                     cn: [
18548                         {
18549                             tag: 'a',
18550                             href: '#',
18551                             cls: 'btn',
18552                             cn: [
18553                                 {
18554                                     tag: 'span',
18555                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18556                                 }
18557                             ]
18558                         }
18559                     ]
18560                 },
18561                 {
18562                     tag: 'td',
18563                     cls: 'separator'
18564                 },
18565                 {
18566                     tag: 'td',
18567                     cn: [
18568                         {
18569                             tag: 'a',
18570                             href: '#',
18571                             cls: 'btn',
18572                             cn: [
18573                                 {
18574                                     tag: 'span',
18575                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18576                                 }
18577                             ]
18578                         }
18579                     ]
18580                 },
18581                 {
18582                     tag: 'td',
18583                     cls: 'separator'
18584                 }
18585             ]
18586         });
18587         
18588     },
18589     
18590     update: function()
18591     {
18592         
18593         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18594         
18595         this.fill();
18596     },
18597     
18598     fill: function() 
18599     {
18600         var hours = this.time.getHours();
18601         var minutes = this.time.getMinutes();
18602         var period = 'AM';
18603         
18604         if(hours > 11){
18605             period = 'PM';
18606         }
18607         
18608         if(hours == 0){
18609             hours = 12;
18610         }
18611         
18612         
18613         if(hours > 12){
18614             hours = hours - 12;
18615         }
18616         
18617         if(hours < 10){
18618             hours = '0' + hours;
18619         }
18620         
18621         if(minutes < 10){
18622             minutes = '0' + minutes;
18623         }
18624         
18625         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18626         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18627         this.pop.select('button', true).first().dom.innerHTML = period;
18628         
18629     },
18630     
18631     place: function()
18632     {   
18633         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18634         
18635         var cls = ['bottom'];
18636         
18637         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18638             cls.pop();
18639             cls.push('top');
18640         }
18641         
18642         cls.push('right');
18643         
18644         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18645             cls.pop();
18646             cls.push('left');
18647         }
18648         
18649         this.picker().addClass(cls.join('-'));
18650         
18651         var _this = this;
18652         
18653         Roo.each(cls, function(c){
18654             if(c == 'bottom'){
18655                 _this.picker().setTop(_this.inputEl().getHeight());
18656                 return;
18657             }
18658             if(c == 'top'){
18659                 _this.picker().setTop(0 - _this.picker().getHeight());
18660                 return;
18661             }
18662             
18663             if(c == 'left'){
18664                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18665                 return;
18666             }
18667             if(c == 'right'){
18668                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18669                 return;
18670             }
18671         });
18672         
18673     },
18674   
18675     onFocus : function()
18676     {
18677         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18678         this.show();
18679     },
18680     
18681     onBlur : function()
18682     {
18683         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18684         this.hide();
18685     },
18686     
18687     show : function()
18688     {
18689         this.picker().show();
18690         this.pop.show();
18691         this.update();
18692         this.place();
18693         
18694         this.fireEvent('show', this, this.date);
18695     },
18696     
18697     hide : function()
18698     {
18699         this.picker().hide();
18700         this.pop.hide();
18701         
18702         this.fireEvent('hide', this, this.date);
18703     },
18704     
18705     setTime : function()
18706     {
18707         this.hide();
18708         this.setValue(this.time.format(this.format));
18709         
18710         this.fireEvent('select', this, this.date);
18711         
18712         
18713     },
18714     
18715     onMousedown: function(e){
18716         e.stopPropagation();
18717         e.preventDefault();
18718     },
18719     
18720     onIncrementHours: function()
18721     {
18722         Roo.log('onIncrementHours');
18723         this.time = this.time.add(Date.HOUR, 1);
18724         this.update();
18725         
18726     },
18727     
18728     onDecrementHours: function()
18729     {
18730         Roo.log('onDecrementHours');
18731         this.time = this.time.add(Date.HOUR, -1);
18732         this.update();
18733     },
18734     
18735     onIncrementMinutes: function()
18736     {
18737         Roo.log('onIncrementMinutes');
18738         this.time = this.time.add(Date.MINUTE, 1);
18739         this.update();
18740     },
18741     
18742     onDecrementMinutes: function()
18743     {
18744         Roo.log('onDecrementMinutes');
18745         this.time = this.time.add(Date.MINUTE, -1);
18746         this.update();
18747     },
18748     
18749     onTogglePeriod: function()
18750     {
18751         Roo.log('onTogglePeriod');
18752         this.time = this.time.add(Date.HOUR, 12);
18753         this.update();
18754     }
18755     
18756    
18757 });
18758
18759 Roo.apply(Roo.bootstrap.TimeField,  {
18760     
18761     content : {
18762         tag: 'tbody',
18763         cn: [
18764             {
18765                 tag: 'tr',
18766                 cn: [
18767                 {
18768                     tag: 'td',
18769                     colspan: '7'
18770                 }
18771                 ]
18772             }
18773         ]
18774     },
18775     
18776     footer : {
18777         tag: 'tfoot',
18778         cn: [
18779             {
18780                 tag: 'tr',
18781                 cn: [
18782                 {
18783                     tag: 'th',
18784                     colspan: '7',
18785                     cls: '',
18786                     cn: [
18787                         {
18788                             tag: 'button',
18789                             cls: 'btn btn-info ok',
18790                             html: 'OK'
18791                         }
18792                     ]
18793                 }
18794
18795                 ]
18796             }
18797         ]
18798     }
18799 });
18800
18801 Roo.apply(Roo.bootstrap.TimeField,  {
18802   
18803     template : {
18804         tag: 'div',
18805         cls: 'datepicker dropdown-menu',
18806         cn: [
18807             {
18808                 tag: 'div',
18809                 cls: 'datepicker-time',
18810                 cn: [
18811                 {
18812                     tag: 'table',
18813                     cls: 'table-condensed',
18814                     cn:[
18815                     Roo.bootstrap.TimeField.content,
18816                     Roo.bootstrap.TimeField.footer
18817                     ]
18818                 }
18819                 ]
18820             }
18821         ]
18822     }
18823 });
18824
18825  
18826
18827  /*
18828  * - LGPL
18829  *
18830  * MonthField
18831  * 
18832  */
18833
18834 /**
18835  * @class Roo.bootstrap.MonthField
18836  * @extends Roo.bootstrap.Input
18837  * Bootstrap MonthField class
18838  * 
18839  * @cfg {String} language default en
18840  * 
18841  * @constructor
18842  * Create a new MonthField
18843  * @param {Object} config The config object
18844  */
18845
18846 Roo.bootstrap.MonthField = function(config){
18847     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18848     
18849     this.addEvents({
18850         /**
18851          * @event show
18852          * Fires when this field show.
18853          * @param {Roo.bootstrap.MonthField} this
18854          * @param {Mixed} date The date value
18855          */
18856         show : true,
18857         /**
18858          * @event show
18859          * Fires when this field hide.
18860          * @param {Roo.bootstrap.MonthField} this
18861          * @param {Mixed} date The date value
18862          */
18863         hide : true,
18864         /**
18865          * @event select
18866          * Fires when select a date.
18867          * @param {Roo.bootstrap.MonthField} this
18868          * @param {String} oldvalue The old value
18869          * @param {String} newvalue The new value
18870          */
18871         select : true
18872     });
18873 };
18874
18875 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18876     
18877     onRender: function(ct, position)
18878     {
18879         
18880         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18881         
18882         this.language = this.language || 'en';
18883         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18884         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18885         
18886         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18887         this.isInline = false;
18888         this.isInput = true;
18889         this.component = this.el.select('.add-on', true).first() || false;
18890         this.component = (this.component && this.component.length === 0) ? false : this.component;
18891         this.hasInput = this.component && this.inputEL().length;
18892         
18893         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18894         
18895         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896         
18897         this.picker().on('mousedown', this.onMousedown, this);
18898         this.picker().on('click', this.onClick, this);
18899         
18900         this.picker().addClass('datepicker-dropdown');
18901         
18902         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18903             v.setStyle('width', '189px');
18904         });
18905         
18906         this.fillMonths();
18907         
18908         this.update();
18909         
18910         if(this.isInline) {
18911             this.show();
18912         }
18913         
18914     },
18915     
18916     setValue: function(v, suppressEvent)
18917     {   
18918         var o = this.getValue();
18919         
18920         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18921         
18922         this.update();
18923
18924         if(suppressEvent !== true){
18925             this.fireEvent('select', this, o, v);
18926         }
18927         
18928     },
18929     
18930     getValue: function()
18931     {
18932         return this.value;
18933     },
18934     
18935     onClick: function(e) 
18936     {
18937         e.stopPropagation();
18938         e.preventDefault();
18939         
18940         var target = e.getTarget();
18941         
18942         if(target.nodeName.toLowerCase() === 'i'){
18943             target = Roo.get(target).dom.parentNode;
18944         }
18945         
18946         var nodeName = target.nodeName;
18947         var className = target.className;
18948         var html = target.innerHTML;
18949         
18950         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18951             return;
18952         }
18953         
18954         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18955         
18956         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18957         
18958         this.hide();
18959                         
18960     },
18961     
18962     picker : function()
18963     {
18964         return this.pickerEl;
18965     },
18966     
18967     fillMonths: function()
18968     {    
18969         var i = 0;
18970         var months = this.picker().select('>.datepicker-months td', true).first();
18971         
18972         months.dom.innerHTML = '';
18973         
18974         while (i < 12) {
18975             var month = {
18976                 tag: 'span',
18977                 cls: 'month',
18978                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18979             };
18980             
18981             months.createChild(month);
18982         }
18983         
18984     },
18985     
18986     update: function()
18987     {
18988         var _this = this;
18989         
18990         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18991             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18992         }
18993         
18994         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18995             e.removeClass('active');
18996             
18997             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18998                 e.addClass('active');
18999             }
19000         })
19001     },
19002     
19003     place: function()
19004     {
19005         if(this.isInline) {
19006             return;
19007         }
19008         
19009         this.picker().removeClass(['bottom', 'top']);
19010         
19011         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19012             /*
19013              * place to the top of element!
19014              *
19015              */
19016             
19017             this.picker().addClass('top');
19018             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19019             
19020             return;
19021         }
19022         
19023         this.picker().addClass('bottom');
19024         
19025         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19026     },
19027     
19028     onFocus : function()
19029     {
19030         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19031         this.show();
19032     },
19033     
19034     onBlur : function()
19035     {
19036         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19037         
19038         var d = this.inputEl().getValue();
19039         
19040         this.setValue(d);
19041                 
19042         this.hide();
19043     },
19044     
19045     show : function()
19046     {
19047         this.picker().show();
19048         this.picker().select('>.datepicker-months', true).first().show();
19049         this.update();
19050         this.place();
19051         
19052         this.fireEvent('show', this, this.date);
19053     },
19054     
19055     hide : function()
19056     {
19057         if(this.isInline) {
19058             return;
19059         }
19060         this.picker().hide();
19061         this.fireEvent('hide', this, this.date);
19062         
19063     },
19064     
19065     onMousedown: function(e)
19066     {
19067         e.stopPropagation();
19068         e.preventDefault();
19069     },
19070     
19071     keyup: function(e)
19072     {
19073         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19074         this.update();
19075     },
19076
19077     fireKey: function(e)
19078     {
19079         if (!this.picker().isVisible()){
19080             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19081                 this.show();
19082             }
19083             return;
19084         }
19085         
19086         var dir;
19087         
19088         switch(e.keyCode){
19089             case 27: // escape
19090                 this.hide();
19091                 e.preventDefault();
19092                 break;
19093             case 37: // left
19094             case 39: // right
19095                 dir = e.keyCode == 37 ? -1 : 1;
19096                 
19097                 this.vIndex = this.vIndex + dir;
19098                 
19099                 if(this.vIndex < 0){
19100                     this.vIndex = 0;
19101                 }
19102                 
19103                 if(this.vIndex > 11){
19104                     this.vIndex = 11;
19105                 }
19106                 
19107                 if(isNaN(this.vIndex)){
19108                     this.vIndex = 0;
19109                 }
19110                 
19111                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19112                 
19113                 break;
19114             case 38: // up
19115             case 40: // down
19116                 
19117                 dir = e.keyCode == 38 ? -1 : 1;
19118                 
19119                 this.vIndex = this.vIndex + dir * 4;
19120                 
19121                 if(this.vIndex < 0){
19122                     this.vIndex = 0;
19123                 }
19124                 
19125                 if(this.vIndex > 11){
19126                     this.vIndex = 11;
19127                 }
19128                 
19129                 if(isNaN(this.vIndex)){
19130                     this.vIndex = 0;
19131                 }
19132                 
19133                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19134                 break;
19135                 
19136             case 13: // enter
19137                 
19138                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19139                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19140                 }
19141                 
19142                 this.hide();
19143                 e.preventDefault();
19144                 break;
19145             case 9: // tab
19146                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19147                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19148                 }
19149                 this.hide();
19150                 break;
19151             case 16: // shift
19152             case 17: // ctrl
19153             case 18: // alt
19154                 break;
19155             default :
19156                 this.hide();
19157                 
19158         }
19159     },
19160     
19161     remove: function() 
19162     {
19163         this.picker().remove();
19164     }
19165    
19166 });
19167
19168 Roo.apply(Roo.bootstrap.MonthField,  {
19169     
19170     content : {
19171         tag: 'tbody',
19172         cn: [
19173         {
19174             tag: 'tr',
19175             cn: [
19176             {
19177                 tag: 'td',
19178                 colspan: '7'
19179             }
19180             ]
19181         }
19182         ]
19183     },
19184     
19185     dates:{
19186         en: {
19187             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19188             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19189         }
19190     }
19191 });
19192
19193 Roo.apply(Roo.bootstrap.MonthField,  {
19194   
19195     template : {
19196         tag: 'div',
19197         cls: 'datepicker dropdown-menu roo-dynamic',
19198         cn: [
19199             {
19200                 tag: 'div',
19201                 cls: 'datepicker-months',
19202                 cn: [
19203                 {
19204                     tag: 'table',
19205                     cls: 'table-condensed',
19206                     cn:[
19207                         Roo.bootstrap.DateField.content
19208                     ]
19209                 }
19210                 ]
19211             }
19212         ]
19213     }
19214 });
19215
19216  
19217
19218  
19219  /*
19220  * - LGPL
19221  *
19222  * CheckBox
19223  * 
19224  */
19225
19226 /**
19227  * @class Roo.bootstrap.CheckBox
19228  * @extends Roo.bootstrap.Input
19229  * Bootstrap CheckBox class
19230  * 
19231  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19232  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19233  * @cfg {String} boxLabel The text that appears beside the checkbox
19234  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19235  * @cfg {Boolean} checked initnal the element
19236  * @cfg {Boolean} inline inline the element (default false)
19237  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19238  * 
19239  * @constructor
19240  * Create a new CheckBox
19241  * @param {Object} config The config object
19242  */
19243
19244 Roo.bootstrap.CheckBox = function(config){
19245     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19246    
19247     this.addEvents({
19248         /**
19249         * @event check
19250         * Fires when the element is checked or unchecked.
19251         * @param {Roo.bootstrap.CheckBox} this This input
19252         * @param {Boolean} checked The new checked value
19253         */
19254        check : true
19255     });
19256     
19257 };
19258
19259 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19260   
19261     inputType: 'checkbox',
19262     inputValue: 1,
19263     valueOff: 0,
19264     boxLabel: false,
19265     checked: false,
19266     weight : false,
19267     inline: false,
19268     
19269     getAutoCreate : function()
19270     {
19271         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19272         
19273         var id = Roo.id();
19274         
19275         var cfg = {};
19276         
19277         cfg.cls = 'form-group ' + this.inputType; //input-group
19278         
19279         if(this.inline){
19280             cfg.cls += ' ' + this.inputType + '-inline';
19281         }
19282         
19283         var input =  {
19284             tag: 'input',
19285             id : id,
19286             type : this.inputType,
19287             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19288             cls : 'roo-' + this.inputType, //'form-box',
19289             placeholder : this.placeholder || ''
19290             
19291         };
19292         
19293         if (this.weight) { // Validity check?
19294             cfg.cls += " " + this.inputType + "-" + this.weight;
19295         }
19296         
19297         if (this.disabled) {
19298             input.disabled=true;
19299         }
19300         
19301         if(this.checked){
19302             input.checked = this.checked;
19303         }
19304         
19305         if (this.name) {
19306             input.name = this.name;
19307         }
19308         
19309         if (this.size) {
19310             input.cls += ' input-' + this.size;
19311         }
19312         
19313         var settings=this;
19314         
19315         ['xs','sm','md','lg'].map(function(size){
19316             if (settings[size]) {
19317                 cfg.cls += ' col-' + size + '-' + settings[size];
19318             }
19319         });
19320         
19321         var inputblock = input;
19322          
19323         if (this.before || this.after) {
19324             
19325             inputblock = {
19326                 cls : 'input-group',
19327                 cn :  [] 
19328             };
19329             
19330             if (this.before) {
19331                 inputblock.cn.push({
19332                     tag :'span',
19333                     cls : 'input-group-addon',
19334                     html : this.before
19335                 });
19336             }
19337             
19338             inputblock.cn.push(input);
19339             
19340             if (this.after) {
19341                 inputblock.cn.push({
19342                     tag :'span',
19343                     cls : 'input-group-addon',
19344                     html : this.after
19345                 });
19346             }
19347             
19348         }
19349         
19350         if (align ==='left' && this.fieldLabel.length) {
19351 //                Roo.log("left and has label");
19352                 cfg.cn = [
19353                     
19354                     {
19355                         tag: 'label',
19356                         'for' :  id,
19357                         cls : 'control-label col-md-' + this.labelWidth,
19358                         html : this.fieldLabel
19359                         
19360                     },
19361                     {
19362                         cls : "col-md-" + (12 - this.labelWidth), 
19363                         cn: [
19364                             inputblock
19365                         ]
19366                     }
19367                     
19368                 ];
19369         } else if ( this.fieldLabel.length) {
19370 //                Roo.log(" label");
19371                 cfg.cn = [
19372                    
19373                     {
19374                         tag: this.boxLabel ? 'span' : 'label',
19375                         'for': id,
19376                         cls: 'control-label box-input-label',
19377                         //cls : 'input-group-addon',
19378                         html : this.fieldLabel
19379                         
19380                     },
19381                     
19382                     inputblock
19383                     
19384                 ];
19385
19386         } else {
19387             
19388 //                Roo.log(" no label && no align");
19389                 cfg.cn = [  inputblock ] ;
19390                 
19391                 
19392         }
19393         
19394         if(this.boxLabel){
19395              var boxLabelCfg = {
19396                 tag: 'label',
19397                 //'for': id, // box label is handled by onclick - so no for...
19398                 cls: 'box-label',
19399                 html: this.boxLabel
19400             };
19401             
19402             if(this.tooltip){
19403                 boxLabelCfg.tooltip = this.tooltip;
19404             }
19405              
19406             cfg.cn.push(boxLabelCfg);
19407         }
19408         
19409         
19410        
19411         return cfg;
19412         
19413     },
19414     
19415     /**
19416      * return the real input element.
19417      */
19418     inputEl: function ()
19419     {
19420         return this.el.select('input.roo-' + this.inputType,true).first();
19421     },
19422     
19423     labelEl: function()
19424     {
19425         return this.el.select('label.control-label',true).first();
19426     },
19427     /* depricated... */
19428     
19429     label: function()
19430     {
19431         return this.labelEl();
19432     },
19433     
19434     boxLabelEl: function()
19435     {
19436         return this.el.select('label.box-label',true).first();
19437     },
19438     
19439     initEvents : function()
19440     {
19441 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19442         
19443         this.inputEl().on('click', this.onClick,  this);
19444         
19445         if (this.boxLabel) { 
19446             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19447         }
19448         
19449         this.startValue = this.getValue();
19450         
19451         if(this.groupId){
19452             Roo.bootstrap.CheckBox.register(this);
19453         }
19454     },
19455     
19456     onClick : function()
19457     {   
19458         this.setChecked(!this.checked);
19459     },
19460     
19461     setChecked : function(state,suppressEvent)
19462     {
19463         this.startValue = this.getValue();
19464         
19465         if(this.inputType == 'radio'){
19466             
19467             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19468                 e.dom.checked = false;
19469             });
19470             
19471             this.inputEl().dom.checked = true;
19472             
19473             this.inputEl().dom.value = this.inputValue;
19474             
19475             if(suppressEvent !== true){
19476                 this.fireEvent('check', this, true);
19477             }
19478             
19479             this.validate();
19480             
19481             return;
19482         }
19483         
19484         this.checked = state;
19485         
19486         this.inputEl().dom.checked = state;
19487         
19488         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19489         
19490         if(suppressEvent !== true){
19491             this.fireEvent('check', this, state);
19492         }
19493         
19494         this.validate();
19495     },
19496     
19497     getValue : function()
19498     {
19499         if(this.inputType == 'radio'){
19500             return this.getGroupValue();
19501         }
19502         
19503         return this.inputEl().getValue();
19504         
19505     },
19506     
19507     getGroupValue : function()
19508     {
19509         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19510             return '';
19511         }
19512         
19513         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19514     },
19515     
19516     setValue : function(v,suppressEvent)
19517     {
19518         if(this.inputType == 'radio'){
19519             this.setGroupValue(v, suppressEvent);
19520             return;
19521         }
19522         
19523         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19524         
19525         this.validate();
19526     },
19527     
19528     setGroupValue : function(v, suppressEvent)
19529     {
19530         this.startValue = this.getValue();
19531         
19532         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19533             e.dom.checked = false;
19534             
19535             if(e.dom.value == v){
19536                 e.dom.checked = true;
19537             }
19538         });
19539         
19540         if(suppressEvent !== true){
19541             this.fireEvent('check', this, true);
19542         }
19543
19544         this.validate();
19545         
19546         return;
19547     },
19548     
19549     validate : function()
19550     {
19551         if(
19552                 this.disabled || 
19553                 (this.inputType == 'radio' && this.validateRadio()) ||
19554                 (this.inputType == 'checkbox' && this.validateCheckbox())
19555         ){
19556             this.markValid();
19557             return true;
19558         }
19559         
19560         this.markInvalid();
19561         return false;
19562     },
19563     
19564     validateRadio : function()
19565     {
19566         if(this.allowBlank){
19567             return true;
19568         }
19569         
19570         var valid = false;
19571         
19572         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19573             if(!e.dom.checked){
19574                 return;
19575             }
19576             
19577             valid = true;
19578             
19579             return false;
19580         });
19581         
19582         return valid;
19583     },
19584     
19585     validateCheckbox : function()
19586     {
19587         if(!this.groupId){
19588             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19589         }
19590         
19591         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19592         
19593         if(!group){
19594             return false;
19595         }
19596         
19597         var r = false;
19598         
19599         for(var i in group){
19600             if(r){
19601                 break;
19602             }
19603             
19604             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19605         }
19606         
19607         return r;
19608     },
19609     
19610     /**
19611      * Mark this field as valid
19612      */
19613     markValid : function()
19614     {
19615         if(this.allowBlank){
19616             return;
19617         }
19618         
19619         var _this = this;
19620         
19621         this.fireEvent('valid', this);
19622         
19623         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19624         
19625         if(this.groupId){
19626             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19627         }
19628         
19629         if(label){
19630             label.markValid();
19631         }
19632         
19633         if(this.inputType == 'radio'){
19634             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19635                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19636                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19637             });
19638             
19639             return;
19640         }
19641         
19642         if(!this.groupId){
19643             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19644             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19645             return;
19646         }
19647         
19648         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19649             
19650         if(!group){
19651             return;
19652         }
19653         
19654         for(var i in group){
19655             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19656             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19657         }
19658     },
19659     
19660      /**
19661      * Mark this field as invalid
19662      * @param {String} msg The validation message
19663      */
19664     markInvalid : function(msg)
19665     {
19666         if(this.allowBlank){
19667             return;
19668         }
19669         
19670         var _this = this;
19671         
19672         this.fireEvent('invalid', this, msg);
19673         
19674         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19675         
19676         if(this.groupId){
19677             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19678         }
19679         
19680         if(label){
19681             label.markInvalid();
19682         }
19683             
19684         if(this.inputType == 'radio'){
19685             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19686                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19687                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19688             });
19689             
19690             return;
19691         }
19692         
19693         if(!this.groupId){
19694             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19695             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19696             return;
19697         }
19698         
19699         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19700         
19701         if(!group){
19702             return;
19703         }
19704         
19705         for(var i in group){
19706             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19707             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19708         }
19709         
19710     },
19711     
19712     disable : function()
19713     {
19714         if(this.inputType != 'radio'){
19715             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19716             return;
19717         }
19718         
19719         var _this = this;
19720         
19721         if(this.rendered){
19722             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19723                 _this.getActionEl().addClass(this.disabledClass);
19724                 e.dom.disabled = true;
19725             });
19726         }
19727         
19728         this.disabled = true;
19729         this.fireEvent("disable", this);
19730         return this;
19731     },
19732
19733     enable : function()
19734     {
19735         if(this.inputType != 'radio'){
19736             Roo.bootstrap.CheckBox.superclass.enable.call(this);
19737             return;
19738         }
19739         
19740         var _this = this;
19741         
19742         if(this.rendered){
19743             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19744                 _this.getActionEl().removeClass(this.disabledClass);
19745                 e.dom.disabled = false;
19746             });
19747         }
19748         
19749         this.disabled = false;
19750         this.fireEvent("enable", this);
19751         return this;
19752     }
19753     
19754
19755 });
19756
19757 Roo.apply(Roo.bootstrap.CheckBox, {
19758     
19759     groups: {},
19760     
19761      /**
19762     * register a CheckBox Group
19763     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19764     */
19765     register : function(checkbox)
19766     {
19767         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19768             this.groups[checkbox.groupId] = {};
19769         }
19770         
19771         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19772             return;
19773         }
19774         
19775         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19776         
19777     },
19778     /**
19779     * fetch a CheckBox Group based on the group ID
19780     * @param {string} the group ID
19781     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19782     */
19783     get: function(groupId) {
19784         if (typeof(this.groups[groupId]) == 'undefined') {
19785             return false;
19786         }
19787         
19788         return this.groups[groupId] ;
19789     }
19790     
19791     
19792 });
19793 /*
19794  * - LGPL
19795  *
19796  * Radio
19797  *
19798  *
19799  * not inline
19800  *<div class="radio">
19801   <label>
19802     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19803     Option one is this and that&mdash;be sure to include why it's great
19804   </label>
19805 </div>
19806  *
19807  *
19808  *inline
19809  *<span>
19810  *<label class="radio-inline">fieldLabel</label>
19811  *<label class="radio-inline">
19812   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19813 </label>
19814 <span>
19815  * 
19816  * 
19817  */
19818
19819 /**
19820  * @class Roo.bootstrap.Radio
19821  * @extends Roo.bootstrap.CheckBox
19822  * Bootstrap Radio class
19823
19824  * @constructor
19825  * Create a new Radio
19826  * @param {Object} config The config object
19827  */
19828
19829 Roo.bootstrap.Radio = function(config){
19830     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19831    
19832 };
19833
19834 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19835     
19836     inputType: 'radio',
19837     inputValue: '',
19838     valueOff: '',
19839     
19840     getAutoCreate : function()
19841     {
19842         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19843         align = align || 'left'; // default...
19844         
19845         
19846         
19847         var id = Roo.id();
19848         
19849         var cfg = {
19850                 tag : this.inline ? 'span' : 'div',
19851                 cls : '',
19852                 cn : []
19853         };
19854         
19855         var inline = this.inline ? ' radio-inline' : '';
19856         
19857         var lbl = {
19858                 tag: 'label' ,
19859                 // does not need for, as we wrap the input with it..
19860                 'for' : id,
19861                 cls : 'control-label box-label' + inline,
19862                 cn : []
19863         };
19864         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19865         
19866         var fieldLabel = {
19867             tag: 'label' ,
19868             //cls : 'control-label' + inline,
19869             html : this.fieldLabel,
19870             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19871         };
19872         
19873  
19874         
19875         
19876         var input =  {
19877             tag: 'input',
19878             id : id,
19879             type : this.inputType,
19880             //value : (!this.checked) ? this.valueOff : this.inputValue,
19881             value : this.inputValue,
19882             cls : 'roo-radio',
19883             placeholder : this.placeholder || '' // ?? needed????
19884             
19885         };
19886         if (this.weight) { // Validity check?
19887             input.cls += " radio-" + this.weight;
19888         }
19889         if (this.disabled) {
19890             input.disabled=true;
19891         }
19892         
19893         if(this.checked){
19894             input.checked = this.checked;
19895         }
19896         
19897         if (this.name) {
19898             input.name = this.name;
19899         }
19900         
19901         if (this.size) {
19902             input.cls += ' input-' + this.size;
19903         }
19904         
19905         //?? can span's inline have a width??
19906         
19907         var settings=this;
19908         ['xs','sm','md','lg'].map(function(size){
19909             if (settings[size]) {
19910                 cfg.cls += ' col-' + size + '-' + settings[size];
19911             }
19912         });
19913         
19914         var inputblock = input;
19915         
19916         if (this.before || this.after) {
19917             
19918             inputblock = {
19919                 cls : 'input-group',
19920                 tag : 'span',
19921                 cn :  [] 
19922             };
19923             if (this.before) {
19924                 inputblock.cn.push({
19925                     tag :'span',
19926                     cls : 'input-group-addon',
19927                     html : this.before
19928                 });
19929             }
19930             inputblock.cn.push(input);
19931             if (this.after) {
19932                 inputblock.cn.push({
19933                     tag :'span',
19934                     cls : 'input-group-addon',
19935                     html : this.after
19936                 });
19937             }
19938             
19939         };
19940         
19941         
19942         if (this.fieldLabel && this.fieldLabel.length) {
19943             cfg.cn.push(fieldLabel);
19944         }
19945        
19946         // normal bootstrap puts the input inside the label.
19947         // however with our styled version - it has to go after the input.
19948        
19949         //lbl.cn.push(inputblock);
19950         
19951         var lblwrap =  {
19952             tag: 'span',
19953             cls: 'radio' + inline,
19954             cn: [
19955                 inputblock,
19956                 lbl
19957             ]
19958         };
19959         
19960         cfg.cn.push( lblwrap);
19961         
19962         if(this.boxLabel){
19963             lbl.cn.push({
19964                 tag: 'span',
19965                 html: this.boxLabel
19966             })
19967         }
19968          
19969         
19970         return cfg;
19971         
19972     },
19973     
19974     initEvents : function()
19975     {
19976 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19977         
19978         this.inputEl().on('click', this.onClick,  this);
19979         if (this.boxLabel) {
19980             //Roo.log('find label');
19981             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19982         }
19983         
19984     },
19985     
19986     inputEl: function ()
19987     {
19988         return this.el.select('input.roo-radio',true).first();
19989     },
19990     onClick : function()
19991     {   
19992         Roo.log("click");
19993         this.setChecked(true);
19994     },
19995     
19996     setChecked : function(state,suppressEvent)
19997     {
19998         if(state){
19999             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20000                 v.dom.checked = false;
20001             });
20002         }
20003         Roo.log(this.inputEl().dom);
20004         this.checked = state;
20005         this.inputEl().dom.checked = state;
20006         
20007         if(suppressEvent !== true){
20008             this.fireEvent('check', this, state);
20009         }
20010         
20011         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20012         
20013     },
20014     
20015     getGroupValue : function()
20016     {
20017         var value = '';
20018         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20019             if(v.dom.checked == true){
20020                 value = v.dom.value;
20021             }
20022         });
20023         
20024         return value;
20025     },
20026     
20027     /**
20028      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20029      * @return {Mixed} value The field value
20030      */
20031     getValue : function(){
20032         return this.getGroupValue();
20033     }
20034     
20035 });
20036
20037  
20038 //<script type="text/javascript">
20039
20040 /*
20041  * Based  Ext JS Library 1.1.1
20042  * Copyright(c) 2006-2007, Ext JS, LLC.
20043  * LGPL
20044  *
20045  */
20046  
20047 /**
20048  * @class Roo.HtmlEditorCore
20049  * @extends Roo.Component
20050  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20051  *
20052  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20053  */
20054
20055 Roo.HtmlEditorCore = function(config){
20056     
20057     
20058     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20059     
20060     
20061     this.addEvents({
20062         /**
20063          * @event initialize
20064          * Fires when the editor is fully initialized (including the iframe)
20065          * @param {Roo.HtmlEditorCore} this
20066          */
20067         initialize: true,
20068         /**
20069          * @event activate
20070          * Fires when the editor is first receives the focus. Any insertion must wait
20071          * until after this event.
20072          * @param {Roo.HtmlEditorCore} this
20073          */
20074         activate: true,
20075          /**
20076          * @event beforesync
20077          * Fires before the textarea is updated with content from the editor iframe. Return false
20078          * to cancel the sync.
20079          * @param {Roo.HtmlEditorCore} this
20080          * @param {String} html
20081          */
20082         beforesync: true,
20083          /**
20084          * @event beforepush
20085          * Fires before the iframe editor is updated with content from the textarea. Return false
20086          * to cancel the push.
20087          * @param {Roo.HtmlEditorCore} this
20088          * @param {String} html
20089          */
20090         beforepush: true,
20091          /**
20092          * @event sync
20093          * Fires when the textarea is updated with content from the editor iframe.
20094          * @param {Roo.HtmlEditorCore} this
20095          * @param {String} html
20096          */
20097         sync: true,
20098          /**
20099          * @event push
20100          * Fires when the iframe editor is updated with content from the textarea.
20101          * @param {Roo.HtmlEditorCore} this
20102          * @param {String} html
20103          */
20104         push: true,
20105         
20106         /**
20107          * @event editorevent
20108          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20109          * @param {Roo.HtmlEditorCore} this
20110          */
20111         editorevent: true
20112         
20113     });
20114     
20115     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20116     
20117     // defaults : white / black...
20118     this.applyBlacklists();
20119     
20120     
20121     
20122 };
20123
20124
20125 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20126
20127
20128      /**
20129      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20130      */
20131     
20132     owner : false,
20133     
20134      /**
20135      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20136      *                        Roo.resizable.
20137      */
20138     resizable : false,
20139      /**
20140      * @cfg {Number} height (in pixels)
20141      */   
20142     height: 300,
20143    /**
20144      * @cfg {Number} width (in pixels)
20145      */   
20146     width: 500,
20147     
20148     /**
20149      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20150      * 
20151      */
20152     stylesheets: false,
20153     
20154     // id of frame..
20155     frameId: false,
20156     
20157     // private properties
20158     validationEvent : false,
20159     deferHeight: true,
20160     initialized : false,
20161     activated : false,
20162     sourceEditMode : false,
20163     onFocus : Roo.emptyFn,
20164     iframePad:3,
20165     hideMode:'offsets',
20166     
20167     clearUp: true,
20168     
20169     // blacklist + whitelisted elements..
20170     black: false,
20171     white: false,
20172      
20173     
20174
20175     /**
20176      * Protected method that will not generally be called directly. It
20177      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20178      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20179      */
20180     getDocMarkup : function(){
20181         // body styles..
20182         var st = '';
20183         
20184         // inherit styels from page...?? 
20185         if (this.stylesheets === false) {
20186             
20187             Roo.get(document.head).select('style').each(function(node) {
20188                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20189             });
20190             
20191             Roo.get(document.head).select('link').each(function(node) { 
20192                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20193             });
20194             
20195         } else if (!this.stylesheets.length) {
20196                 // simple..
20197                 st = '<style type="text/css">' +
20198                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20199                    '</style>';
20200         } else { 
20201             
20202         }
20203         
20204         st +=  '<style type="text/css">' +
20205             'IMG { cursor: pointer } ' +
20206         '</style>';
20207
20208         
20209         return '<html><head>' + st  +
20210             //<style type="text/css">' +
20211             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20212             //'</style>' +
20213             ' </head><body class="roo-htmleditor-body"></body></html>';
20214     },
20215
20216     // private
20217     onRender : function(ct, position)
20218     {
20219         var _t = this;
20220         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20221         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20222         
20223         
20224         this.el.dom.style.border = '0 none';
20225         this.el.dom.setAttribute('tabIndex', -1);
20226         this.el.addClass('x-hidden hide');
20227         
20228         
20229         
20230         if(Roo.isIE){ // fix IE 1px bogus margin
20231             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20232         }
20233        
20234         
20235         this.frameId = Roo.id();
20236         
20237          
20238         
20239         var iframe = this.owner.wrap.createChild({
20240             tag: 'iframe',
20241             cls: 'form-control', // bootstrap..
20242             id: this.frameId,
20243             name: this.frameId,
20244             frameBorder : 'no',
20245             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20246         }, this.el
20247         );
20248         
20249         
20250         this.iframe = iframe.dom;
20251
20252          this.assignDocWin();
20253         
20254         this.doc.designMode = 'on';
20255        
20256         this.doc.open();
20257         this.doc.write(this.getDocMarkup());
20258         this.doc.close();
20259
20260         
20261         var task = { // must defer to wait for browser to be ready
20262             run : function(){
20263                 //console.log("run task?" + this.doc.readyState);
20264                 this.assignDocWin();
20265                 if(this.doc.body || this.doc.readyState == 'complete'){
20266                     try {
20267                         this.doc.designMode="on";
20268                     } catch (e) {
20269                         return;
20270                     }
20271                     Roo.TaskMgr.stop(task);
20272                     this.initEditor.defer(10, this);
20273                 }
20274             },
20275             interval : 10,
20276             duration: 10000,
20277             scope: this
20278         };
20279         Roo.TaskMgr.start(task);
20280
20281     },
20282
20283     // private
20284     onResize : function(w, h)
20285     {
20286          Roo.log('resize: ' +w + ',' + h );
20287         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20288         if(!this.iframe){
20289             return;
20290         }
20291         if(typeof w == 'number'){
20292             
20293             this.iframe.style.width = w + 'px';
20294         }
20295         if(typeof h == 'number'){
20296             
20297             this.iframe.style.height = h + 'px';
20298             if(this.doc){
20299                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20300             }
20301         }
20302         
20303     },
20304
20305     /**
20306      * Toggles the editor between standard and source edit mode.
20307      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20308      */
20309     toggleSourceEdit : function(sourceEditMode){
20310         
20311         this.sourceEditMode = sourceEditMode === true;
20312         
20313         if(this.sourceEditMode){
20314  
20315             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20316             
20317         }else{
20318             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20319             //this.iframe.className = '';
20320             this.deferFocus();
20321         }
20322         //this.setSize(this.owner.wrap.getSize());
20323         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20324     },
20325
20326     
20327   
20328
20329     /**
20330      * Protected method that will not generally be called directly. If you need/want
20331      * custom HTML cleanup, this is the method you should override.
20332      * @param {String} html The HTML to be cleaned
20333      * return {String} The cleaned HTML
20334      */
20335     cleanHtml : function(html){
20336         html = String(html);
20337         if(html.length > 5){
20338             if(Roo.isSafari){ // strip safari nonsense
20339                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20340             }
20341         }
20342         if(html == '&nbsp;'){
20343             html = '';
20344         }
20345         return html;
20346     },
20347
20348     /**
20349      * HTML Editor -> Textarea
20350      * Protected method that will not generally be called directly. Syncs the contents
20351      * of the editor iframe with the textarea.
20352      */
20353     syncValue : function(){
20354         if(this.initialized){
20355             var bd = (this.doc.body || this.doc.documentElement);
20356             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20357             var html = bd.innerHTML;
20358             if(Roo.isSafari){
20359                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20360                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20361                 if(m && m[1]){
20362                     html = '<div style="'+m[0]+'">' + html + '</div>';
20363                 }
20364             }
20365             html = this.cleanHtml(html);
20366             // fix up the special chars.. normaly like back quotes in word...
20367             // however we do not want to do this with chinese..
20368             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20369                 var cc = b.charCodeAt();
20370                 if (
20371                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20372                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20373                     (cc >= 0xf900 && cc < 0xfb00 )
20374                 ) {
20375                         return b;
20376                 }
20377                 return "&#"+cc+";" 
20378             });
20379             if(this.owner.fireEvent('beforesync', this, html) !== false){
20380                 this.el.dom.value = html;
20381                 this.owner.fireEvent('sync', this, html);
20382             }
20383         }
20384     },
20385
20386     /**
20387      * Protected method that will not generally be called directly. Pushes the value of the textarea
20388      * into the iframe editor.
20389      */
20390     pushValue : function(){
20391         if(this.initialized){
20392             var v = this.el.dom.value.trim();
20393             
20394 //            if(v.length < 1){
20395 //                v = '&#160;';
20396 //            }
20397             
20398             if(this.owner.fireEvent('beforepush', this, v) !== false){
20399                 var d = (this.doc.body || this.doc.documentElement);
20400                 d.innerHTML = v;
20401                 this.cleanUpPaste();
20402                 this.el.dom.value = d.innerHTML;
20403                 this.owner.fireEvent('push', this, v);
20404             }
20405         }
20406     },
20407
20408     // private
20409     deferFocus : function(){
20410         this.focus.defer(10, this);
20411     },
20412
20413     // doc'ed in Field
20414     focus : function(){
20415         if(this.win && !this.sourceEditMode){
20416             this.win.focus();
20417         }else{
20418             this.el.focus();
20419         }
20420     },
20421     
20422     assignDocWin: function()
20423     {
20424         var iframe = this.iframe;
20425         
20426          if(Roo.isIE){
20427             this.doc = iframe.contentWindow.document;
20428             this.win = iframe.contentWindow;
20429         } else {
20430 //            if (!Roo.get(this.frameId)) {
20431 //                return;
20432 //            }
20433 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20434 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20435             
20436             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20437                 return;
20438             }
20439             
20440             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20441             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20442         }
20443     },
20444     
20445     // private
20446     initEditor : function(){
20447         //console.log("INIT EDITOR");
20448         this.assignDocWin();
20449         
20450         
20451         
20452         this.doc.designMode="on";
20453         this.doc.open();
20454         this.doc.write(this.getDocMarkup());
20455         this.doc.close();
20456         
20457         var dbody = (this.doc.body || this.doc.documentElement);
20458         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20459         // this copies styles from the containing element into thsi one..
20460         // not sure why we need all of this..
20461         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20462         
20463         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20464         //ss['background-attachment'] = 'fixed'; // w3c
20465         dbody.bgProperties = 'fixed'; // ie
20466         //Roo.DomHelper.applyStyles(dbody, ss);
20467         Roo.EventManager.on(this.doc, {
20468             //'mousedown': this.onEditorEvent,
20469             'mouseup': this.onEditorEvent,
20470             'dblclick': this.onEditorEvent,
20471             'click': this.onEditorEvent,
20472             'keyup': this.onEditorEvent,
20473             buffer:100,
20474             scope: this
20475         });
20476         if(Roo.isGecko){
20477             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20478         }
20479         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20480             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20481         }
20482         this.initialized = true;
20483
20484         this.owner.fireEvent('initialize', this);
20485         this.pushValue();
20486     },
20487
20488     // private
20489     onDestroy : function(){
20490         
20491         
20492         
20493         if(this.rendered){
20494             
20495             //for (var i =0; i < this.toolbars.length;i++) {
20496             //    // fixme - ask toolbars for heights?
20497             //    this.toolbars[i].onDestroy();
20498            // }
20499             
20500             //this.wrap.dom.innerHTML = '';
20501             //this.wrap.remove();
20502         }
20503     },
20504
20505     // private
20506     onFirstFocus : function(){
20507         
20508         this.assignDocWin();
20509         
20510         
20511         this.activated = true;
20512          
20513     
20514         if(Roo.isGecko){ // prevent silly gecko errors
20515             this.win.focus();
20516             var s = this.win.getSelection();
20517             if(!s.focusNode || s.focusNode.nodeType != 3){
20518                 var r = s.getRangeAt(0);
20519                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20520                 r.collapse(true);
20521                 this.deferFocus();
20522             }
20523             try{
20524                 this.execCmd('useCSS', true);
20525                 this.execCmd('styleWithCSS', false);
20526             }catch(e){}
20527         }
20528         this.owner.fireEvent('activate', this);
20529     },
20530
20531     // private
20532     adjustFont: function(btn){
20533         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20534         //if(Roo.isSafari){ // safari
20535         //    adjust *= 2;
20536        // }
20537         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20538         if(Roo.isSafari){ // safari
20539             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20540             v =  (v < 10) ? 10 : v;
20541             v =  (v > 48) ? 48 : v;
20542             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20543             
20544         }
20545         
20546         
20547         v = Math.max(1, v+adjust);
20548         
20549         this.execCmd('FontSize', v  );
20550     },
20551
20552     onEditorEvent : function(e)
20553     {
20554         this.owner.fireEvent('editorevent', this, e);
20555       //  this.updateToolbar();
20556         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20557     },
20558
20559     insertTag : function(tg)
20560     {
20561         // could be a bit smarter... -> wrap the current selected tRoo..
20562         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20563             
20564             range = this.createRange(this.getSelection());
20565             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20566             wrappingNode.appendChild(range.extractContents());
20567             range.insertNode(wrappingNode);
20568
20569             return;
20570             
20571             
20572             
20573         }
20574         this.execCmd("formatblock",   tg);
20575         
20576     },
20577     
20578     insertText : function(txt)
20579     {
20580         
20581         
20582         var range = this.createRange();
20583         range.deleteContents();
20584                //alert(Sender.getAttribute('label'));
20585                
20586         range.insertNode(this.doc.createTextNode(txt));
20587     } ,
20588     
20589      
20590
20591     /**
20592      * Executes a Midas editor command on the editor document and performs necessary focus and
20593      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20594      * @param {String} cmd The Midas command
20595      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20596      */
20597     relayCmd : function(cmd, value){
20598         this.win.focus();
20599         this.execCmd(cmd, value);
20600         this.owner.fireEvent('editorevent', this);
20601         //this.updateToolbar();
20602         this.owner.deferFocus();
20603     },
20604
20605     /**
20606      * Executes a Midas editor command directly on the editor document.
20607      * For visual commands, you should use {@link #relayCmd} instead.
20608      * <b>This should only be called after the editor is initialized.</b>
20609      * @param {String} cmd The Midas command
20610      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20611      */
20612     execCmd : function(cmd, value){
20613         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20614         this.syncValue();
20615     },
20616  
20617  
20618    
20619     /**
20620      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20621      * to insert tRoo.
20622      * @param {String} text | dom node.. 
20623      */
20624     insertAtCursor : function(text)
20625     {
20626         
20627         
20628         
20629         if(!this.activated){
20630             return;
20631         }
20632         /*
20633         if(Roo.isIE){
20634             this.win.focus();
20635             var r = this.doc.selection.createRange();
20636             if(r){
20637                 r.collapse(true);
20638                 r.pasteHTML(text);
20639                 this.syncValue();
20640                 this.deferFocus();
20641             
20642             }
20643             return;
20644         }
20645         */
20646         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20647             this.win.focus();
20648             
20649             
20650             // from jquery ui (MIT licenced)
20651             var range, node;
20652             var win = this.win;
20653             
20654             if (win.getSelection && win.getSelection().getRangeAt) {
20655                 range = win.getSelection().getRangeAt(0);
20656                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20657                 range.insertNode(node);
20658             } else if (win.document.selection && win.document.selection.createRange) {
20659                 // no firefox support
20660                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20661                 win.document.selection.createRange().pasteHTML(txt);
20662             } else {
20663                 // no firefox support
20664                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20665                 this.execCmd('InsertHTML', txt);
20666             } 
20667             
20668             this.syncValue();
20669             
20670             this.deferFocus();
20671         }
20672     },
20673  // private
20674     mozKeyPress : function(e){
20675         if(e.ctrlKey){
20676             var c = e.getCharCode(), cmd;
20677           
20678             if(c > 0){
20679                 c = String.fromCharCode(c).toLowerCase();
20680                 switch(c){
20681                     case 'b':
20682                         cmd = 'bold';
20683                         break;
20684                     case 'i':
20685                         cmd = 'italic';
20686                         break;
20687                     
20688                     case 'u':
20689                         cmd = 'underline';
20690                         break;
20691                     
20692                     case 'v':
20693                         this.cleanUpPaste.defer(100, this);
20694                         return;
20695                         
20696                 }
20697                 if(cmd){
20698                     this.win.focus();
20699                     this.execCmd(cmd);
20700                     this.deferFocus();
20701                     e.preventDefault();
20702                 }
20703                 
20704             }
20705         }
20706     },
20707
20708     // private
20709     fixKeys : function(){ // load time branching for fastest keydown performance
20710         if(Roo.isIE){
20711             return function(e){
20712                 var k = e.getKey(), r;
20713                 if(k == e.TAB){
20714                     e.stopEvent();
20715                     r = this.doc.selection.createRange();
20716                     if(r){
20717                         r.collapse(true);
20718                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20719                         this.deferFocus();
20720                     }
20721                     return;
20722                 }
20723                 
20724                 if(k == e.ENTER){
20725                     r = this.doc.selection.createRange();
20726                     if(r){
20727                         var target = r.parentElement();
20728                         if(!target || target.tagName.toLowerCase() != 'li'){
20729                             e.stopEvent();
20730                             r.pasteHTML('<br />');
20731                             r.collapse(false);
20732                             r.select();
20733                         }
20734                     }
20735                 }
20736                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20737                     this.cleanUpPaste.defer(100, this);
20738                     return;
20739                 }
20740                 
20741                 
20742             };
20743         }else if(Roo.isOpera){
20744             return function(e){
20745                 var k = e.getKey();
20746                 if(k == e.TAB){
20747                     e.stopEvent();
20748                     this.win.focus();
20749                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20750                     this.deferFocus();
20751                 }
20752                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20753                     this.cleanUpPaste.defer(100, this);
20754                     return;
20755                 }
20756                 
20757             };
20758         }else if(Roo.isSafari){
20759             return function(e){
20760                 var k = e.getKey();
20761                 
20762                 if(k == e.TAB){
20763                     e.stopEvent();
20764                     this.execCmd('InsertText','\t');
20765                     this.deferFocus();
20766                     return;
20767                 }
20768                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20769                     this.cleanUpPaste.defer(100, this);
20770                     return;
20771                 }
20772                 
20773              };
20774         }
20775     }(),
20776     
20777     getAllAncestors: function()
20778     {
20779         var p = this.getSelectedNode();
20780         var a = [];
20781         if (!p) {
20782             a.push(p); // push blank onto stack..
20783             p = this.getParentElement();
20784         }
20785         
20786         
20787         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20788             a.push(p);
20789             p = p.parentNode;
20790         }
20791         a.push(this.doc.body);
20792         return a;
20793     },
20794     lastSel : false,
20795     lastSelNode : false,
20796     
20797     
20798     getSelection : function() 
20799     {
20800         this.assignDocWin();
20801         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20802     },
20803     
20804     getSelectedNode: function() 
20805     {
20806         // this may only work on Gecko!!!
20807         
20808         // should we cache this!!!!
20809         
20810         
20811         
20812          
20813         var range = this.createRange(this.getSelection()).cloneRange();
20814         
20815         if (Roo.isIE) {
20816             var parent = range.parentElement();
20817             while (true) {
20818                 var testRange = range.duplicate();
20819                 testRange.moveToElementText(parent);
20820                 if (testRange.inRange(range)) {
20821                     break;
20822                 }
20823                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20824                     break;
20825                 }
20826                 parent = parent.parentElement;
20827             }
20828             return parent;
20829         }
20830         
20831         // is ancestor a text element.
20832         var ac =  range.commonAncestorContainer;
20833         if (ac.nodeType == 3) {
20834             ac = ac.parentNode;
20835         }
20836         
20837         var ar = ac.childNodes;
20838          
20839         var nodes = [];
20840         var other_nodes = [];
20841         var has_other_nodes = false;
20842         for (var i=0;i<ar.length;i++) {
20843             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20844                 continue;
20845             }
20846             // fullly contained node.
20847             
20848             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20849                 nodes.push(ar[i]);
20850                 continue;
20851             }
20852             
20853             // probably selected..
20854             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20855                 other_nodes.push(ar[i]);
20856                 continue;
20857             }
20858             // outer..
20859             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20860                 continue;
20861             }
20862             
20863             
20864             has_other_nodes = true;
20865         }
20866         if (!nodes.length && other_nodes.length) {
20867             nodes= other_nodes;
20868         }
20869         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20870             return false;
20871         }
20872         
20873         return nodes[0];
20874     },
20875     createRange: function(sel)
20876     {
20877         // this has strange effects when using with 
20878         // top toolbar - not sure if it's a great idea.
20879         //this.editor.contentWindow.focus();
20880         if (typeof sel != "undefined") {
20881             try {
20882                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20883             } catch(e) {
20884                 return this.doc.createRange();
20885             }
20886         } else {
20887             return this.doc.createRange();
20888         }
20889     },
20890     getParentElement: function()
20891     {
20892         
20893         this.assignDocWin();
20894         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20895         
20896         var range = this.createRange(sel);
20897          
20898         try {
20899             var p = range.commonAncestorContainer;
20900             while (p.nodeType == 3) { // text node
20901                 p = p.parentNode;
20902             }
20903             return p;
20904         } catch (e) {
20905             return null;
20906         }
20907     
20908     },
20909     /***
20910      *
20911      * Range intersection.. the hard stuff...
20912      *  '-1' = before
20913      *  '0' = hits..
20914      *  '1' = after.
20915      *         [ -- selected range --- ]
20916      *   [fail]                        [fail]
20917      *
20918      *    basically..
20919      *      if end is before start or  hits it. fail.
20920      *      if start is after end or hits it fail.
20921      *
20922      *   if either hits (but other is outside. - then it's not 
20923      *   
20924      *    
20925      **/
20926     
20927     
20928     // @see http://www.thismuchiknow.co.uk/?p=64.
20929     rangeIntersectsNode : function(range, node)
20930     {
20931         var nodeRange = node.ownerDocument.createRange();
20932         try {
20933             nodeRange.selectNode(node);
20934         } catch (e) {
20935             nodeRange.selectNodeContents(node);
20936         }
20937     
20938         var rangeStartRange = range.cloneRange();
20939         rangeStartRange.collapse(true);
20940     
20941         var rangeEndRange = range.cloneRange();
20942         rangeEndRange.collapse(false);
20943     
20944         var nodeStartRange = nodeRange.cloneRange();
20945         nodeStartRange.collapse(true);
20946     
20947         var nodeEndRange = nodeRange.cloneRange();
20948         nodeEndRange.collapse(false);
20949     
20950         return rangeStartRange.compareBoundaryPoints(
20951                  Range.START_TO_START, nodeEndRange) == -1 &&
20952                rangeEndRange.compareBoundaryPoints(
20953                  Range.START_TO_START, nodeStartRange) == 1;
20954         
20955          
20956     },
20957     rangeCompareNode : function(range, node)
20958     {
20959         var nodeRange = node.ownerDocument.createRange();
20960         try {
20961             nodeRange.selectNode(node);
20962         } catch (e) {
20963             nodeRange.selectNodeContents(node);
20964         }
20965         
20966         
20967         range.collapse(true);
20968     
20969         nodeRange.collapse(true);
20970      
20971         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20972         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20973          
20974         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20975         
20976         var nodeIsBefore   =  ss == 1;
20977         var nodeIsAfter    = ee == -1;
20978         
20979         if (nodeIsBefore && nodeIsAfter) {
20980             return 0; // outer
20981         }
20982         if (!nodeIsBefore && nodeIsAfter) {
20983             return 1; //right trailed.
20984         }
20985         
20986         if (nodeIsBefore && !nodeIsAfter) {
20987             return 2;  // left trailed.
20988         }
20989         // fully contined.
20990         return 3;
20991     },
20992
20993     // private? - in a new class?
20994     cleanUpPaste :  function()
20995     {
20996         // cleans up the whole document..
20997         Roo.log('cleanuppaste');
20998         
20999         this.cleanUpChildren(this.doc.body);
21000         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21001         if (clean != this.doc.body.innerHTML) {
21002             this.doc.body.innerHTML = clean;
21003         }
21004         
21005     },
21006     
21007     cleanWordChars : function(input) {// change the chars to hex code
21008         var he = Roo.HtmlEditorCore;
21009         
21010         var output = input;
21011         Roo.each(he.swapCodes, function(sw) { 
21012             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21013             
21014             output = output.replace(swapper, sw[1]);
21015         });
21016         
21017         return output;
21018     },
21019     
21020     
21021     cleanUpChildren : function (n)
21022     {
21023         if (!n.childNodes.length) {
21024             return;
21025         }
21026         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21027            this.cleanUpChild(n.childNodes[i]);
21028         }
21029     },
21030     
21031     
21032         
21033     
21034     cleanUpChild : function (node)
21035     {
21036         var ed = this;
21037         //console.log(node);
21038         if (node.nodeName == "#text") {
21039             // clean up silly Windows -- stuff?
21040             return; 
21041         }
21042         if (node.nodeName == "#comment") {
21043             node.parentNode.removeChild(node);
21044             // clean up silly Windows -- stuff?
21045             return; 
21046         }
21047         var lcname = node.tagName.toLowerCase();
21048         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21049         // whitelist of tags..
21050         
21051         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21052             // remove node.
21053             node.parentNode.removeChild(node);
21054             return;
21055             
21056         }
21057         
21058         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21059         
21060         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21061         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21062         
21063         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21064         //    remove_keep_children = true;
21065         //}
21066         
21067         if (remove_keep_children) {
21068             this.cleanUpChildren(node);
21069             // inserts everything just before this node...
21070             while (node.childNodes.length) {
21071                 var cn = node.childNodes[0];
21072                 node.removeChild(cn);
21073                 node.parentNode.insertBefore(cn, node);
21074             }
21075             node.parentNode.removeChild(node);
21076             return;
21077         }
21078         
21079         if (!node.attributes || !node.attributes.length) {
21080             this.cleanUpChildren(node);
21081             return;
21082         }
21083         
21084         function cleanAttr(n,v)
21085         {
21086             
21087             if (v.match(/^\./) || v.match(/^\//)) {
21088                 return;
21089             }
21090             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21091                 return;
21092             }
21093             if (v.match(/^#/)) {
21094                 return;
21095             }
21096 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21097             node.removeAttribute(n);
21098             
21099         }
21100         
21101         var cwhite = this.cwhite;
21102         var cblack = this.cblack;
21103             
21104         function cleanStyle(n,v)
21105         {
21106             if (v.match(/expression/)) { //XSS?? should we even bother..
21107                 node.removeAttribute(n);
21108                 return;
21109             }
21110             
21111             var parts = v.split(/;/);
21112             var clean = [];
21113             
21114             Roo.each(parts, function(p) {
21115                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21116                 if (!p.length) {
21117                     return true;
21118                 }
21119                 var l = p.split(':').shift().replace(/\s+/g,'');
21120                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21121                 
21122                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21123 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21124                     //node.removeAttribute(n);
21125                     return true;
21126                 }
21127                 //Roo.log()
21128                 // only allow 'c whitelisted system attributes'
21129                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21130 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21131                     //node.removeAttribute(n);
21132                     return true;
21133                 }
21134                 
21135                 
21136                  
21137                 
21138                 clean.push(p);
21139                 return true;
21140             });
21141             if (clean.length) { 
21142                 node.setAttribute(n, clean.join(';'));
21143             } else {
21144                 node.removeAttribute(n);
21145             }
21146             
21147         }
21148         
21149         
21150         for (var i = node.attributes.length-1; i > -1 ; i--) {
21151             var a = node.attributes[i];
21152             //console.log(a);
21153             
21154             if (a.name.toLowerCase().substr(0,2)=='on')  {
21155                 node.removeAttribute(a.name);
21156                 continue;
21157             }
21158             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21159                 node.removeAttribute(a.name);
21160                 continue;
21161             }
21162             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21163                 cleanAttr(a.name,a.value); // fixme..
21164                 continue;
21165             }
21166             if (a.name == 'style') {
21167                 cleanStyle(a.name,a.value);
21168                 continue;
21169             }
21170             /// clean up MS crap..
21171             // tecnically this should be a list of valid class'es..
21172             
21173             
21174             if (a.name == 'class') {
21175                 if (a.value.match(/^Mso/)) {
21176                     node.className = '';
21177                 }
21178                 
21179                 if (a.value.match(/body/)) {
21180                     node.className = '';
21181                 }
21182                 continue;
21183             }
21184             
21185             // style cleanup!?
21186             // class cleanup?
21187             
21188         }
21189         
21190         
21191         this.cleanUpChildren(node);
21192         
21193         
21194     },
21195     
21196     /**
21197      * Clean up MS wordisms...
21198      */
21199     cleanWord : function(node)
21200     {
21201         
21202         
21203         if (!node) {
21204             this.cleanWord(this.doc.body);
21205             return;
21206         }
21207         if (node.nodeName == "#text") {
21208             // clean up silly Windows -- stuff?
21209             return; 
21210         }
21211         if (node.nodeName == "#comment") {
21212             node.parentNode.removeChild(node);
21213             // clean up silly Windows -- stuff?
21214             return; 
21215         }
21216         
21217         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21218             node.parentNode.removeChild(node);
21219             return;
21220         }
21221         
21222         // remove - but keep children..
21223         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21224             while (node.childNodes.length) {
21225                 var cn = node.childNodes[0];
21226                 node.removeChild(cn);
21227                 node.parentNode.insertBefore(cn, node);
21228             }
21229             node.parentNode.removeChild(node);
21230             this.iterateChildren(node, this.cleanWord);
21231             return;
21232         }
21233         // clean styles
21234         if (node.className.length) {
21235             
21236             var cn = node.className.split(/\W+/);
21237             var cna = [];
21238             Roo.each(cn, function(cls) {
21239                 if (cls.match(/Mso[a-zA-Z]+/)) {
21240                     return;
21241                 }
21242                 cna.push(cls);
21243             });
21244             node.className = cna.length ? cna.join(' ') : '';
21245             if (!cna.length) {
21246                 node.removeAttribute("class");
21247             }
21248         }
21249         
21250         if (node.hasAttribute("lang")) {
21251             node.removeAttribute("lang");
21252         }
21253         
21254         if (node.hasAttribute("style")) {
21255             
21256             var styles = node.getAttribute("style").split(";");
21257             var nstyle = [];
21258             Roo.each(styles, function(s) {
21259                 if (!s.match(/:/)) {
21260                     return;
21261                 }
21262                 var kv = s.split(":");
21263                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21264                     return;
21265                 }
21266                 // what ever is left... we allow.
21267                 nstyle.push(s);
21268             });
21269             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21270             if (!nstyle.length) {
21271                 node.removeAttribute('style');
21272             }
21273         }
21274         this.iterateChildren(node, this.cleanWord);
21275         
21276         
21277         
21278     },
21279     /**
21280      * iterateChildren of a Node, calling fn each time, using this as the scole..
21281      * @param {DomNode} node node to iterate children of.
21282      * @param {Function} fn method of this class to call on each item.
21283      */
21284     iterateChildren : function(node, fn)
21285     {
21286         if (!node.childNodes.length) {
21287                 return;
21288         }
21289         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21290            fn.call(this, node.childNodes[i])
21291         }
21292     },
21293     
21294     
21295     /**
21296      * cleanTableWidths.
21297      *
21298      * Quite often pasting from word etc.. results in tables with column and widths.
21299      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21300      *
21301      */
21302     cleanTableWidths : function(node)
21303     {
21304          
21305          
21306         if (!node) {
21307             this.cleanTableWidths(this.doc.body);
21308             return;
21309         }
21310         
21311         // ignore list...
21312         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21313             return; 
21314         }
21315         Roo.log(node.tagName);
21316         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21317             this.iterateChildren(node, this.cleanTableWidths);
21318             return;
21319         }
21320         if (node.hasAttribute('width')) {
21321             node.removeAttribute('width');
21322         }
21323         
21324          
21325         if (node.hasAttribute("style")) {
21326             // pretty basic...
21327             
21328             var styles = node.getAttribute("style").split(";");
21329             var nstyle = [];
21330             Roo.each(styles, function(s) {
21331                 if (!s.match(/:/)) {
21332                     return;
21333                 }
21334                 var kv = s.split(":");
21335                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21336                     return;
21337                 }
21338                 // what ever is left... we allow.
21339                 nstyle.push(s);
21340             });
21341             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21342             if (!nstyle.length) {
21343                 node.removeAttribute('style');
21344             }
21345         }
21346         
21347         this.iterateChildren(node, this.cleanTableWidths);
21348         
21349         
21350     },
21351     
21352     
21353     
21354     
21355     domToHTML : function(currentElement, depth, nopadtext) {
21356         
21357         depth = depth || 0;
21358         nopadtext = nopadtext || false;
21359     
21360         if (!currentElement) {
21361             return this.domToHTML(this.doc.body);
21362         }
21363         
21364         //Roo.log(currentElement);
21365         var j;
21366         var allText = false;
21367         var nodeName = currentElement.nodeName;
21368         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21369         
21370         if  (nodeName == '#text') {
21371             
21372             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21373         }
21374         
21375         
21376         var ret = '';
21377         if (nodeName != 'BODY') {
21378              
21379             var i = 0;
21380             // Prints the node tagName, such as <A>, <IMG>, etc
21381             if (tagName) {
21382                 var attr = [];
21383                 for(i = 0; i < currentElement.attributes.length;i++) {
21384                     // quoting?
21385                     var aname = currentElement.attributes.item(i).name;
21386                     if (!currentElement.attributes.item(i).value.length) {
21387                         continue;
21388                     }
21389                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21390                 }
21391                 
21392                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21393             } 
21394             else {
21395                 
21396                 // eack
21397             }
21398         } else {
21399             tagName = false;
21400         }
21401         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21402             return ret;
21403         }
21404         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21405             nopadtext = true;
21406         }
21407         
21408         
21409         // Traverse the tree
21410         i = 0;
21411         var currentElementChild = currentElement.childNodes.item(i);
21412         var allText = true;
21413         var innerHTML  = '';
21414         lastnode = '';
21415         while (currentElementChild) {
21416             // Formatting code (indent the tree so it looks nice on the screen)
21417             var nopad = nopadtext;
21418             if (lastnode == 'SPAN') {
21419                 nopad  = true;
21420             }
21421             // text
21422             if  (currentElementChild.nodeName == '#text') {
21423                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21424                 toadd = nopadtext ? toadd : toadd.trim();
21425                 if (!nopad && toadd.length > 80) {
21426                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21427                 }
21428                 innerHTML  += toadd;
21429                 
21430                 i++;
21431                 currentElementChild = currentElement.childNodes.item(i);
21432                 lastNode = '';
21433                 continue;
21434             }
21435             allText = false;
21436             
21437             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21438                 
21439             // Recursively traverse the tree structure of the child node
21440             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21441             lastnode = currentElementChild.nodeName;
21442             i++;
21443             currentElementChild=currentElement.childNodes.item(i);
21444         }
21445         
21446         ret += innerHTML;
21447         
21448         if (!allText) {
21449                 // The remaining code is mostly for formatting the tree
21450             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21451         }
21452         
21453         
21454         if (tagName) {
21455             ret+= "</"+tagName+">";
21456         }
21457         return ret;
21458         
21459     },
21460         
21461     applyBlacklists : function()
21462     {
21463         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21464         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21465         
21466         this.white = [];
21467         this.black = [];
21468         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21469             if (b.indexOf(tag) > -1) {
21470                 return;
21471             }
21472             this.white.push(tag);
21473             
21474         }, this);
21475         
21476         Roo.each(w, function(tag) {
21477             if (b.indexOf(tag) > -1) {
21478                 return;
21479             }
21480             if (this.white.indexOf(tag) > -1) {
21481                 return;
21482             }
21483             this.white.push(tag);
21484             
21485         }, this);
21486         
21487         
21488         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21489             if (w.indexOf(tag) > -1) {
21490                 return;
21491             }
21492             this.black.push(tag);
21493             
21494         }, this);
21495         
21496         Roo.each(b, function(tag) {
21497             if (w.indexOf(tag) > -1) {
21498                 return;
21499             }
21500             if (this.black.indexOf(tag) > -1) {
21501                 return;
21502             }
21503             this.black.push(tag);
21504             
21505         }, this);
21506         
21507         
21508         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21509         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21510         
21511         this.cwhite = [];
21512         this.cblack = [];
21513         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21514             if (b.indexOf(tag) > -1) {
21515                 return;
21516             }
21517             this.cwhite.push(tag);
21518             
21519         }, this);
21520         
21521         Roo.each(w, function(tag) {
21522             if (b.indexOf(tag) > -1) {
21523                 return;
21524             }
21525             if (this.cwhite.indexOf(tag) > -1) {
21526                 return;
21527             }
21528             this.cwhite.push(tag);
21529             
21530         }, this);
21531         
21532         
21533         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21534             if (w.indexOf(tag) > -1) {
21535                 return;
21536             }
21537             this.cblack.push(tag);
21538             
21539         }, this);
21540         
21541         Roo.each(b, function(tag) {
21542             if (w.indexOf(tag) > -1) {
21543                 return;
21544             }
21545             if (this.cblack.indexOf(tag) > -1) {
21546                 return;
21547             }
21548             this.cblack.push(tag);
21549             
21550         }, this);
21551     },
21552     
21553     setStylesheets : function(stylesheets)
21554     {
21555         if(typeof(stylesheets) == 'string'){
21556             Roo.get(this.iframe.contentDocument.head).createChild({
21557                 tag : 'link',
21558                 rel : 'stylesheet',
21559                 type : 'text/css',
21560                 href : stylesheets
21561             });
21562             
21563             return;
21564         }
21565         var _this = this;
21566      
21567         Roo.each(stylesheets, function(s) {
21568             if(!s.length){
21569                 return;
21570             }
21571             
21572             Roo.get(_this.iframe.contentDocument.head).createChild({
21573                 tag : 'link',
21574                 rel : 'stylesheet',
21575                 type : 'text/css',
21576                 href : s
21577             });
21578         });
21579
21580         
21581     },
21582     
21583     removeStylesheets : function()
21584     {
21585         var _this = this;
21586         
21587         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21588             s.remove();
21589         });
21590     }
21591     
21592     // hide stuff that is not compatible
21593     /**
21594      * @event blur
21595      * @hide
21596      */
21597     /**
21598      * @event change
21599      * @hide
21600      */
21601     /**
21602      * @event focus
21603      * @hide
21604      */
21605     /**
21606      * @event specialkey
21607      * @hide
21608      */
21609     /**
21610      * @cfg {String} fieldClass @hide
21611      */
21612     /**
21613      * @cfg {String} focusClass @hide
21614      */
21615     /**
21616      * @cfg {String} autoCreate @hide
21617      */
21618     /**
21619      * @cfg {String} inputType @hide
21620      */
21621     /**
21622      * @cfg {String} invalidClass @hide
21623      */
21624     /**
21625      * @cfg {String} invalidText @hide
21626      */
21627     /**
21628      * @cfg {String} msgFx @hide
21629      */
21630     /**
21631      * @cfg {String} validateOnBlur @hide
21632      */
21633 });
21634
21635 Roo.HtmlEditorCore.white = [
21636         'area', 'br', 'img', 'input', 'hr', 'wbr',
21637         
21638        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21639        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21640        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21641        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21642        'table',   'ul',         'xmp', 
21643        
21644        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21645       'thead',   'tr', 
21646      
21647       'dir', 'menu', 'ol', 'ul', 'dl',
21648        
21649       'embed',  'object'
21650 ];
21651
21652
21653 Roo.HtmlEditorCore.black = [
21654     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21655         'applet', // 
21656         'base',   'basefont', 'bgsound', 'blink',  'body', 
21657         'frame',  'frameset', 'head',    'html',   'ilayer', 
21658         'iframe', 'layer',  'link',     'meta',    'object',   
21659         'script', 'style' ,'title',  'xml' // clean later..
21660 ];
21661 Roo.HtmlEditorCore.clean = [
21662     'script', 'style', 'title', 'xml'
21663 ];
21664 Roo.HtmlEditorCore.remove = [
21665     'font'
21666 ];
21667 // attributes..
21668
21669 Roo.HtmlEditorCore.ablack = [
21670     'on'
21671 ];
21672     
21673 Roo.HtmlEditorCore.aclean = [ 
21674     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21675 ];
21676
21677 // protocols..
21678 Roo.HtmlEditorCore.pwhite= [
21679         'http',  'https',  'mailto'
21680 ];
21681
21682 // white listed style attributes.
21683 Roo.HtmlEditorCore.cwhite= [
21684       //  'text-align', /// default is to allow most things..
21685       
21686          
21687 //        'font-size'//??
21688 ];
21689
21690 // black listed style attributes.
21691 Roo.HtmlEditorCore.cblack= [
21692       //  'font-size' -- this can be set by the project 
21693 ];
21694
21695
21696 Roo.HtmlEditorCore.swapCodes   =[ 
21697     [    8211, "--" ], 
21698     [    8212, "--" ], 
21699     [    8216,  "'" ],  
21700     [    8217, "'" ],  
21701     [    8220, '"' ],  
21702     [    8221, '"' ],  
21703     [    8226, "*" ],  
21704     [    8230, "..." ]
21705 ]; 
21706
21707     /*
21708  * - LGPL
21709  *
21710  * HtmlEditor
21711  * 
21712  */
21713
21714 /**
21715  * @class Roo.bootstrap.HtmlEditor
21716  * @extends Roo.bootstrap.TextArea
21717  * Bootstrap HtmlEditor class
21718
21719  * @constructor
21720  * Create a new HtmlEditor
21721  * @param {Object} config The config object
21722  */
21723
21724 Roo.bootstrap.HtmlEditor = function(config){
21725     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21726     if (!this.toolbars) {
21727         this.toolbars = [];
21728     }
21729     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21730     this.addEvents({
21731             /**
21732              * @event initialize
21733              * Fires when the editor is fully initialized (including the iframe)
21734              * @param {HtmlEditor} this
21735              */
21736             initialize: true,
21737             /**
21738              * @event activate
21739              * Fires when the editor is first receives the focus. Any insertion must wait
21740              * until after this event.
21741              * @param {HtmlEditor} this
21742              */
21743             activate: true,
21744              /**
21745              * @event beforesync
21746              * Fires before the textarea is updated with content from the editor iframe. Return false
21747              * to cancel the sync.
21748              * @param {HtmlEditor} this
21749              * @param {String} html
21750              */
21751             beforesync: true,
21752              /**
21753              * @event beforepush
21754              * Fires before the iframe editor is updated with content from the textarea. Return false
21755              * to cancel the push.
21756              * @param {HtmlEditor} this
21757              * @param {String} html
21758              */
21759             beforepush: true,
21760              /**
21761              * @event sync
21762              * Fires when the textarea is updated with content from the editor iframe.
21763              * @param {HtmlEditor} this
21764              * @param {String} html
21765              */
21766             sync: true,
21767              /**
21768              * @event push
21769              * Fires when the iframe editor is updated with content from the textarea.
21770              * @param {HtmlEditor} this
21771              * @param {String} html
21772              */
21773             push: true,
21774              /**
21775              * @event editmodechange
21776              * Fires when the editor switches edit modes
21777              * @param {HtmlEditor} this
21778              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21779              */
21780             editmodechange: true,
21781             /**
21782              * @event editorevent
21783              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21784              * @param {HtmlEditor} this
21785              */
21786             editorevent: true,
21787             /**
21788              * @event firstfocus
21789              * Fires when on first focus - needed by toolbars..
21790              * @param {HtmlEditor} this
21791              */
21792             firstfocus: true,
21793             /**
21794              * @event autosave
21795              * Auto save the htmlEditor value as a file into Events
21796              * @param {HtmlEditor} this
21797              */
21798             autosave: true,
21799             /**
21800              * @event savedpreview
21801              * preview the saved version of htmlEditor
21802              * @param {HtmlEditor} this
21803              */
21804             savedpreview: true
21805         });
21806 };
21807
21808
21809 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21810     
21811     
21812       /**
21813      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21814      */
21815     toolbars : false,
21816    
21817      /**
21818      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21819      *                        Roo.resizable.
21820      */
21821     resizable : false,
21822      /**
21823      * @cfg {Number} height (in pixels)
21824      */   
21825     height: 300,
21826    /**
21827      * @cfg {Number} width (in pixels)
21828      */   
21829     width: false,
21830     
21831     /**
21832      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21833      * 
21834      */
21835     stylesheets: false,
21836     
21837     // id of frame..
21838     frameId: false,
21839     
21840     // private properties
21841     validationEvent : false,
21842     deferHeight: true,
21843     initialized : false,
21844     activated : false,
21845     
21846     onFocus : Roo.emptyFn,
21847     iframePad:3,
21848     hideMode:'offsets',
21849     
21850     
21851     tbContainer : false,
21852     
21853     toolbarContainer :function() {
21854         return this.wrap.select('.x-html-editor-tb',true).first();
21855     },
21856
21857     /**
21858      * Protected method that will not generally be called directly. It
21859      * is called when the editor creates its toolbar. Override this method if you need to
21860      * add custom toolbar buttons.
21861      * @param {HtmlEditor} editor
21862      */
21863     createToolbar : function(){
21864         
21865         Roo.log("create toolbars");
21866         
21867         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21868         this.toolbars[0].render(this.toolbarContainer());
21869         
21870         return;
21871         
21872 //        if (!editor.toolbars || !editor.toolbars.length) {
21873 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21874 //        }
21875 //        
21876 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21877 //            editor.toolbars[i] = Roo.factory(
21878 //                    typeof(editor.toolbars[i]) == 'string' ?
21879 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21880 //                Roo.bootstrap.HtmlEditor);
21881 //            editor.toolbars[i].init(editor);
21882 //        }
21883     },
21884
21885      
21886     // private
21887     onRender : function(ct, position)
21888     {
21889        // Roo.log("Call onRender: " + this.xtype);
21890         var _t = this;
21891         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21892       
21893         this.wrap = this.inputEl().wrap({
21894             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21895         });
21896         
21897         this.editorcore.onRender(ct, position);
21898          
21899         if (this.resizable) {
21900             this.resizeEl = new Roo.Resizable(this.wrap, {
21901                 pinned : true,
21902                 wrap: true,
21903                 dynamic : true,
21904                 minHeight : this.height,
21905                 height: this.height,
21906                 handles : this.resizable,
21907                 width: this.width,
21908                 listeners : {
21909                     resize : function(r, w, h) {
21910                         _t.onResize(w,h); // -something
21911                     }
21912                 }
21913             });
21914             
21915         }
21916         this.createToolbar(this);
21917        
21918         
21919         if(!this.width && this.resizable){
21920             this.setSize(this.wrap.getSize());
21921         }
21922         if (this.resizeEl) {
21923             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21924             // should trigger onReize..
21925         }
21926         
21927     },
21928
21929     // private
21930     onResize : function(w, h)
21931     {
21932         Roo.log('resize: ' +w + ',' + h );
21933         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21934         var ew = false;
21935         var eh = false;
21936         
21937         if(this.inputEl() ){
21938             if(typeof w == 'number'){
21939                 var aw = w - this.wrap.getFrameWidth('lr');
21940                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21941                 ew = aw;
21942             }
21943             if(typeof h == 'number'){
21944                  var tbh = -11;  // fixme it needs to tool bar size!
21945                 for (var i =0; i < this.toolbars.length;i++) {
21946                     // fixme - ask toolbars for heights?
21947                     tbh += this.toolbars[i].el.getHeight();
21948                     //if (this.toolbars[i].footer) {
21949                     //    tbh += this.toolbars[i].footer.el.getHeight();
21950                     //}
21951                 }
21952               
21953                 
21954                 
21955                 
21956                 
21957                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21958                 ah -= 5; // knock a few pixes off for look..
21959                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21960                 var eh = ah;
21961             }
21962         }
21963         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21964         this.editorcore.onResize(ew,eh);
21965         
21966     },
21967
21968     /**
21969      * Toggles the editor between standard and source edit mode.
21970      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21971      */
21972     toggleSourceEdit : function(sourceEditMode)
21973     {
21974         this.editorcore.toggleSourceEdit(sourceEditMode);
21975         
21976         if(this.editorcore.sourceEditMode){
21977             Roo.log('editor - showing textarea');
21978             
21979 //            Roo.log('in');
21980 //            Roo.log(this.syncValue());
21981             this.syncValue();
21982             this.inputEl().removeClass(['hide', 'x-hidden']);
21983             this.inputEl().dom.removeAttribute('tabIndex');
21984             this.inputEl().focus();
21985         }else{
21986             Roo.log('editor - hiding textarea');
21987 //            Roo.log('out')
21988 //            Roo.log(this.pushValue()); 
21989             this.pushValue();
21990             
21991             this.inputEl().addClass(['hide', 'x-hidden']);
21992             this.inputEl().dom.setAttribute('tabIndex', -1);
21993             //this.deferFocus();
21994         }
21995          
21996         if(this.resizable){
21997             this.setSize(this.wrap.getSize());
21998         }
21999         
22000         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22001     },
22002  
22003     // private (for BoxComponent)
22004     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22005
22006     // private (for BoxComponent)
22007     getResizeEl : function(){
22008         return this.wrap;
22009     },
22010
22011     // private (for BoxComponent)
22012     getPositionEl : function(){
22013         return this.wrap;
22014     },
22015
22016     // private
22017     initEvents : function(){
22018         this.originalValue = this.getValue();
22019     },
22020
22021 //    /**
22022 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22023 //     * @method
22024 //     */
22025 //    markInvalid : Roo.emptyFn,
22026 //    /**
22027 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22028 //     * @method
22029 //     */
22030 //    clearInvalid : Roo.emptyFn,
22031
22032     setValue : function(v){
22033         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22034         this.editorcore.pushValue();
22035     },
22036
22037      
22038     // private
22039     deferFocus : function(){
22040         this.focus.defer(10, this);
22041     },
22042
22043     // doc'ed in Field
22044     focus : function(){
22045         this.editorcore.focus();
22046         
22047     },
22048       
22049
22050     // private
22051     onDestroy : function(){
22052         
22053         
22054         
22055         if(this.rendered){
22056             
22057             for (var i =0; i < this.toolbars.length;i++) {
22058                 // fixme - ask toolbars for heights?
22059                 this.toolbars[i].onDestroy();
22060             }
22061             
22062             this.wrap.dom.innerHTML = '';
22063             this.wrap.remove();
22064         }
22065     },
22066
22067     // private
22068     onFirstFocus : function(){
22069         //Roo.log("onFirstFocus");
22070         this.editorcore.onFirstFocus();
22071          for (var i =0; i < this.toolbars.length;i++) {
22072             this.toolbars[i].onFirstFocus();
22073         }
22074         
22075     },
22076     
22077     // private
22078     syncValue : function()
22079     {   
22080         this.editorcore.syncValue();
22081     },
22082     
22083     pushValue : function()
22084     {   
22085         this.editorcore.pushValue();
22086     }
22087      
22088     
22089     // hide stuff that is not compatible
22090     /**
22091      * @event blur
22092      * @hide
22093      */
22094     /**
22095      * @event change
22096      * @hide
22097      */
22098     /**
22099      * @event focus
22100      * @hide
22101      */
22102     /**
22103      * @event specialkey
22104      * @hide
22105      */
22106     /**
22107      * @cfg {String} fieldClass @hide
22108      */
22109     /**
22110      * @cfg {String} focusClass @hide
22111      */
22112     /**
22113      * @cfg {String} autoCreate @hide
22114      */
22115     /**
22116      * @cfg {String} inputType @hide
22117      */
22118     /**
22119      * @cfg {String} invalidClass @hide
22120      */
22121     /**
22122      * @cfg {String} invalidText @hide
22123      */
22124     /**
22125      * @cfg {String} msgFx @hide
22126      */
22127     /**
22128      * @cfg {String} validateOnBlur @hide
22129      */
22130 });
22131  
22132     
22133    
22134    
22135    
22136       
22137 Roo.namespace('Roo.bootstrap.htmleditor');
22138 /**
22139  * @class Roo.bootstrap.HtmlEditorToolbar1
22140  * Basic Toolbar
22141  * 
22142  * Usage:
22143  *
22144  new Roo.bootstrap.HtmlEditor({
22145     ....
22146     toolbars : [
22147         new Roo.bootstrap.HtmlEditorToolbar1({
22148             disable : { fonts: 1 , format: 1, ..., ... , ...],
22149             btns : [ .... ]
22150         })
22151     }
22152      
22153  * 
22154  * @cfg {Object} disable List of elements to disable..
22155  * @cfg {Array} btns List of additional buttons.
22156  * 
22157  * 
22158  * NEEDS Extra CSS? 
22159  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22160  */
22161  
22162 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22163 {
22164     
22165     Roo.apply(this, config);
22166     
22167     // default disabled, based on 'good practice'..
22168     this.disable = this.disable || {};
22169     Roo.applyIf(this.disable, {
22170         fontSize : true,
22171         colors : true,
22172         specialElements : true
22173     });
22174     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22175     
22176     this.editor = config.editor;
22177     this.editorcore = config.editor.editorcore;
22178     
22179     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22180     
22181     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22182     // dont call parent... till later.
22183 }
22184 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22185      
22186     bar : true,
22187     
22188     editor : false,
22189     editorcore : false,
22190     
22191     
22192     formats : [
22193         "p" ,  
22194         "h1","h2","h3","h4","h5","h6", 
22195         "pre", "code", 
22196         "abbr", "acronym", "address", "cite", "samp", "var",
22197         'div','span'
22198     ],
22199     
22200     onRender : function(ct, position)
22201     {
22202        // Roo.log("Call onRender: " + this.xtype);
22203         
22204        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22205        Roo.log(this.el);
22206        this.el.dom.style.marginBottom = '0';
22207        var _this = this;
22208        var editorcore = this.editorcore;
22209        var editor= this.editor;
22210        
22211        var children = [];
22212        var btn = function(id,cmd , toggle, handler){
22213        
22214             var  event = toggle ? 'toggle' : 'click';
22215        
22216             var a = {
22217                 size : 'sm',
22218                 xtype: 'Button',
22219                 xns: Roo.bootstrap,
22220                 glyphicon : id,
22221                 cmd : id || cmd,
22222                 enableToggle:toggle !== false,
22223                 //html : 'submit'
22224                 pressed : toggle ? false : null,
22225                 listeners : {}
22226             };
22227             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22228                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22229             };
22230             children.push(a);
22231             return a;
22232        }
22233         
22234         var style = {
22235                 xtype: 'Button',
22236                 size : 'sm',
22237                 xns: Roo.bootstrap,
22238                 glyphicon : 'font',
22239                 //html : 'submit'
22240                 menu : {
22241                     xtype: 'Menu',
22242                     xns: Roo.bootstrap,
22243                     items:  []
22244                 }
22245         };
22246         Roo.each(this.formats, function(f) {
22247             style.menu.items.push({
22248                 xtype :'MenuItem',
22249                 xns: Roo.bootstrap,
22250                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22251                 tagname : f,
22252                 listeners : {
22253                     click : function()
22254                     {
22255                         editorcore.insertTag(this.tagname);
22256                         editor.focus();
22257                     }
22258                 }
22259                 
22260             });
22261         });
22262          children.push(style);   
22263             
22264             
22265         btn('bold',false,true);
22266         btn('italic',false,true);
22267         btn('align-left', 'justifyleft',true);
22268         btn('align-center', 'justifycenter',true);
22269         btn('align-right' , 'justifyright',true);
22270         btn('link', false, false, function(btn) {
22271             //Roo.log("create link?");
22272             var url = prompt(this.createLinkText, this.defaultLinkValue);
22273             if(url && url != 'http:/'+'/'){
22274                 this.editorcore.relayCmd('createlink', url);
22275             }
22276         }),
22277         btn('list','insertunorderedlist',true);
22278         btn('pencil', false,true, function(btn){
22279                 Roo.log(this);
22280                 
22281                 this.toggleSourceEdit(btn.pressed);
22282         });
22283         /*
22284         var cog = {
22285                 xtype: 'Button',
22286                 size : 'sm',
22287                 xns: Roo.bootstrap,
22288                 glyphicon : 'cog',
22289                 //html : 'submit'
22290                 menu : {
22291                     xtype: 'Menu',
22292                     xns: Roo.bootstrap,
22293                     items:  []
22294                 }
22295         };
22296         
22297         cog.menu.items.push({
22298             xtype :'MenuItem',
22299             xns: Roo.bootstrap,
22300             html : Clean styles,
22301             tagname : f,
22302             listeners : {
22303                 click : function()
22304                 {
22305                     editorcore.insertTag(this.tagname);
22306                     editor.focus();
22307                 }
22308             }
22309             
22310         });
22311        */
22312         
22313          
22314        this.xtype = 'NavSimplebar';
22315         
22316         for(var i=0;i< children.length;i++) {
22317             
22318             this.buttons.add(this.addxtypeChild(children[i]));
22319             
22320         }
22321         
22322         editor.on('editorevent', this.updateToolbar, this);
22323     },
22324     onBtnClick : function(id)
22325     {
22326        this.editorcore.relayCmd(id);
22327        this.editorcore.focus();
22328     },
22329     
22330     /**
22331      * Protected method that will not generally be called directly. It triggers
22332      * a toolbar update by reading the markup state of the current selection in the editor.
22333      */
22334     updateToolbar: function(){
22335
22336         if(!this.editorcore.activated){
22337             this.editor.onFirstFocus(); // is this neeed?
22338             return;
22339         }
22340
22341         var btns = this.buttons; 
22342         var doc = this.editorcore.doc;
22343         btns.get('bold').setActive(doc.queryCommandState('bold'));
22344         btns.get('italic').setActive(doc.queryCommandState('italic'));
22345         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22346         
22347         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22348         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22349         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22350         
22351         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22352         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22353          /*
22354         
22355         var ans = this.editorcore.getAllAncestors();
22356         if (this.formatCombo) {
22357             
22358             
22359             var store = this.formatCombo.store;
22360             this.formatCombo.setValue("");
22361             for (var i =0; i < ans.length;i++) {
22362                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22363                     // select it..
22364                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22365                     break;
22366                 }
22367             }
22368         }
22369         
22370         
22371         
22372         // hides menus... - so this cant be on a menu...
22373         Roo.bootstrap.MenuMgr.hideAll();
22374         */
22375         Roo.bootstrap.MenuMgr.hideAll();
22376         //this.editorsyncValue();
22377     },
22378     onFirstFocus: function() {
22379         this.buttons.each(function(item){
22380            item.enable();
22381         });
22382     },
22383     toggleSourceEdit : function(sourceEditMode){
22384         
22385           
22386         if(sourceEditMode){
22387             Roo.log("disabling buttons");
22388            this.buttons.each( function(item){
22389                 if(item.cmd != 'pencil'){
22390                     item.disable();
22391                 }
22392             });
22393           
22394         }else{
22395             Roo.log("enabling buttons");
22396             if(this.editorcore.initialized){
22397                 this.buttons.each( function(item){
22398                     item.enable();
22399                 });
22400             }
22401             
22402         }
22403         Roo.log("calling toggole on editor");
22404         // tell the editor that it's been pressed..
22405         this.editor.toggleSourceEdit(sourceEditMode);
22406        
22407     }
22408 });
22409
22410
22411
22412
22413
22414 /**
22415  * @class Roo.bootstrap.Table.AbstractSelectionModel
22416  * @extends Roo.util.Observable
22417  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22418  * implemented by descendant classes.  This class should not be directly instantiated.
22419  * @constructor
22420  */
22421 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22422     this.locked = false;
22423     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22424 };
22425
22426
22427 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22428     /** @ignore Called by the grid automatically. Do not call directly. */
22429     init : function(grid){
22430         this.grid = grid;
22431         this.initEvents();
22432     },
22433
22434     /**
22435      * Locks the selections.
22436      */
22437     lock : function(){
22438         this.locked = true;
22439     },
22440
22441     /**
22442      * Unlocks the selections.
22443      */
22444     unlock : function(){
22445         this.locked = false;
22446     },
22447
22448     /**
22449      * Returns true if the selections are locked.
22450      * @return {Boolean}
22451      */
22452     isLocked : function(){
22453         return this.locked;
22454     }
22455 });
22456 /**
22457  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22458  * @class Roo.bootstrap.Table.RowSelectionModel
22459  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22460  * It supports multiple selections and keyboard selection/navigation. 
22461  * @constructor
22462  * @param {Object} config
22463  */
22464
22465 Roo.bootstrap.Table.RowSelectionModel = function(config){
22466     Roo.apply(this, config);
22467     this.selections = new Roo.util.MixedCollection(false, function(o){
22468         return o.id;
22469     });
22470
22471     this.last = false;
22472     this.lastActive = false;
22473
22474     this.addEvents({
22475         /**
22476              * @event selectionchange
22477              * Fires when the selection changes
22478              * @param {SelectionModel} this
22479              */
22480             "selectionchange" : true,
22481         /**
22482              * @event afterselectionchange
22483              * Fires after the selection changes (eg. by key press or clicking)
22484              * @param {SelectionModel} this
22485              */
22486             "afterselectionchange" : true,
22487         /**
22488              * @event beforerowselect
22489              * Fires when a row is selected being selected, return false to cancel.
22490              * @param {SelectionModel} this
22491              * @param {Number} rowIndex The selected index
22492              * @param {Boolean} keepExisting False if other selections will be cleared
22493              */
22494             "beforerowselect" : true,
22495         /**
22496              * @event rowselect
22497              * Fires when a row is selected.
22498              * @param {SelectionModel} this
22499              * @param {Number} rowIndex The selected index
22500              * @param {Roo.data.Record} r The record
22501              */
22502             "rowselect" : true,
22503         /**
22504              * @event rowdeselect
22505              * Fires when a row is deselected.
22506              * @param {SelectionModel} this
22507              * @param {Number} rowIndex The selected index
22508              */
22509         "rowdeselect" : true
22510     });
22511     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22512     this.locked = false;
22513  };
22514
22515 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22516     /**
22517      * @cfg {Boolean} singleSelect
22518      * True to allow selection of only one row at a time (defaults to false)
22519      */
22520     singleSelect : false,
22521
22522     // private
22523     initEvents : function()
22524     {
22525
22526         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22527         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22528         //}else{ // allow click to work like normal
22529          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22530         //}
22531         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22532         this.grid.on("rowclick", this.handleMouseDown, this);
22533         
22534         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22535             "up" : function(e){
22536                 if(!e.shiftKey){
22537                     this.selectPrevious(e.shiftKey);
22538                 }else if(this.last !== false && this.lastActive !== false){
22539                     var last = this.last;
22540                     this.selectRange(this.last,  this.lastActive-1);
22541                     this.grid.getView().focusRow(this.lastActive);
22542                     if(last !== false){
22543                         this.last = last;
22544                     }
22545                 }else{
22546                     this.selectFirstRow();
22547                 }
22548                 this.fireEvent("afterselectionchange", this);
22549             },
22550             "down" : function(e){
22551                 if(!e.shiftKey){
22552                     this.selectNext(e.shiftKey);
22553                 }else if(this.last !== false && this.lastActive !== false){
22554                     var last = this.last;
22555                     this.selectRange(this.last,  this.lastActive+1);
22556                     this.grid.getView().focusRow(this.lastActive);
22557                     if(last !== false){
22558                         this.last = last;
22559                     }
22560                 }else{
22561                     this.selectFirstRow();
22562                 }
22563                 this.fireEvent("afterselectionchange", this);
22564             },
22565             scope: this
22566         });
22567         this.grid.store.on('load', function(){
22568             this.selections.clear();
22569         },this);
22570         /*
22571         var view = this.grid.view;
22572         view.on("refresh", this.onRefresh, this);
22573         view.on("rowupdated", this.onRowUpdated, this);
22574         view.on("rowremoved", this.onRemove, this);
22575         */
22576     },
22577
22578     // private
22579     onRefresh : function()
22580     {
22581         var ds = this.grid.store, i, v = this.grid.view;
22582         var s = this.selections;
22583         s.each(function(r){
22584             if((i = ds.indexOfId(r.id)) != -1){
22585                 v.onRowSelect(i);
22586             }else{
22587                 s.remove(r);
22588             }
22589         });
22590     },
22591
22592     // private
22593     onRemove : function(v, index, r){
22594         this.selections.remove(r);
22595     },
22596
22597     // private
22598     onRowUpdated : function(v, index, r){
22599         if(this.isSelected(r)){
22600             v.onRowSelect(index);
22601         }
22602     },
22603
22604     /**
22605      * Select records.
22606      * @param {Array} records The records to select
22607      * @param {Boolean} keepExisting (optional) True to keep existing selections
22608      */
22609     selectRecords : function(records, keepExisting)
22610     {
22611         if(!keepExisting){
22612             this.clearSelections();
22613         }
22614             var ds = this.grid.store;
22615         for(var i = 0, len = records.length; i < len; i++){
22616             this.selectRow(ds.indexOf(records[i]), true);
22617         }
22618     },
22619
22620     /**
22621      * Gets the number of selected rows.
22622      * @return {Number}
22623      */
22624     getCount : function(){
22625         return this.selections.length;
22626     },
22627
22628     /**
22629      * Selects the first row in the grid.
22630      */
22631     selectFirstRow : function(){
22632         this.selectRow(0);
22633     },
22634
22635     /**
22636      * Select the last row.
22637      * @param {Boolean} keepExisting (optional) True to keep existing selections
22638      */
22639     selectLastRow : function(keepExisting){
22640         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22641         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22642     },
22643
22644     /**
22645      * Selects the row immediately following the last selected row.
22646      * @param {Boolean} keepExisting (optional) True to keep existing selections
22647      */
22648     selectNext : function(keepExisting)
22649     {
22650             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22651             this.selectRow(this.last+1, keepExisting);
22652             this.grid.getView().focusRow(this.last);
22653         }
22654     },
22655
22656     /**
22657      * Selects the row that precedes the last selected row.
22658      * @param {Boolean} keepExisting (optional) True to keep existing selections
22659      */
22660     selectPrevious : function(keepExisting){
22661         if(this.last){
22662             this.selectRow(this.last-1, keepExisting);
22663             this.grid.getView().focusRow(this.last);
22664         }
22665     },
22666
22667     /**
22668      * Returns the selected records
22669      * @return {Array} Array of selected records
22670      */
22671     getSelections : function(){
22672         return [].concat(this.selections.items);
22673     },
22674
22675     /**
22676      * Returns the first selected record.
22677      * @return {Record}
22678      */
22679     getSelected : function(){
22680         return this.selections.itemAt(0);
22681     },
22682
22683
22684     /**
22685      * Clears all selections.
22686      */
22687     clearSelections : function(fast)
22688     {
22689         if(this.locked) {
22690             return;
22691         }
22692         if(fast !== true){
22693                 var ds = this.grid.store;
22694             var s = this.selections;
22695             s.each(function(r){
22696                 this.deselectRow(ds.indexOfId(r.id));
22697             }, this);
22698             s.clear();
22699         }else{
22700             this.selections.clear();
22701         }
22702         this.last = false;
22703     },
22704
22705
22706     /**
22707      * Selects all rows.
22708      */
22709     selectAll : function(){
22710         if(this.locked) {
22711             return;
22712         }
22713         this.selections.clear();
22714         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22715             this.selectRow(i, true);
22716         }
22717     },
22718
22719     /**
22720      * Returns True if there is a selection.
22721      * @return {Boolean}
22722      */
22723     hasSelection : function(){
22724         return this.selections.length > 0;
22725     },
22726
22727     /**
22728      * Returns True if the specified row is selected.
22729      * @param {Number/Record} record The record or index of the record to check
22730      * @return {Boolean}
22731      */
22732     isSelected : function(index){
22733             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22734         return (r && this.selections.key(r.id) ? true : false);
22735     },
22736
22737     /**
22738      * Returns True if the specified record id is selected.
22739      * @param {String} id The id of record to check
22740      * @return {Boolean}
22741      */
22742     isIdSelected : function(id){
22743         return (this.selections.key(id) ? true : false);
22744     },
22745
22746
22747     // private
22748     handleMouseDBClick : function(e, t){
22749         
22750     },
22751     // private
22752     handleMouseDown : function(e, t)
22753     {
22754             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22755         if(this.isLocked() || rowIndex < 0 ){
22756             return;
22757         };
22758         if(e.shiftKey && this.last !== false){
22759             var last = this.last;
22760             this.selectRange(last, rowIndex, e.ctrlKey);
22761             this.last = last; // reset the last
22762             t.focus();
22763     
22764         }else{
22765             var isSelected = this.isSelected(rowIndex);
22766             //Roo.log("select row:" + rowIndex);
22767             if(isSelected){
22768                 this.deselectRow(rowIndex);
22769             } else {
22770                         this.selectRow(rowIndex, true);
22771             }
22772     
22773             /*
22774                 if(e.button !== 0 && isSelected){
22775                 alert('rowIndex 2: ' + rowIndex);
22776                     view.focusRow(rowIndex);
22777                 }else if(e.ctrlKey && isSelected){
22778                     this.deselectRow(rowIndex);
22779                 }else if(!isSelected){
22780                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22781                     view.focusRow(rowIndex);
22782                 }
22783             */
22784         }
22785         this.fireEvent("afterselectionchange", this);
22786     },
22787     // private
22788     handleDragableRowClick :  function(grid, rowIndex, e) 
22789     {
22790         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22791             this.selectRow(rowIndex, false);
22792             grid.view.focusRow(rowIndex);
22793              this.fireEvent("afterselectionchange", this);
22794         }
22795     },
22796     
22797     /**
22798      * Selects multiple rows.
22799      * @param {Array} rows Array of the indexes of the row to select
22800      * @param {Boolean} keepExisting (optional) True to keep existing selections
22801      */
22802     selectRows : function(rows, keepExisting){
22803         if(!keepExisting){
22804             this.clearSelections();
22805         }
22806         for(var i = 0, len = rows.length; i < len; i++){
22807             this.selectRow(rows[i], true);
22808         }
22809     },
22810
22811     /**
22812      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22813      * @param {Number} startRow The index of the first row in the range
22814      * @param {Number} endRow The index of the last row in the range
22815      * @param {Boolean} keepExisting (optional) True to retain existing selections
22816      */
22817     selectRange : function(startRow, endRow, keepExisting){
22818         if(this.locked) {
22819             return;
22820         }
22821         if(!keepExisting){
22822             this.clearSelections();
22823         }
22824         if(startRow <= endRow){
22825             for(var i = startRow; i <= endRow; i++){
22826                 this.selectRow(i, true);
22827             }
22828         }else{
22829             for(var i = startRow; i >= endRow; i--){
22830                 this.selectRow(i, true);
22831             }
22832         }
22833     },
22834
22835     /**
22836      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22837      * @param {Number} startRow The index of the first row in the range
22838      * @param {Number} endRow The index of the last row in the range
22839      */
22840     deselectRange : function(startRow, endRow, preventViewNotify){
22841         if(this.locked) {
22842             return;
22843         }
22844         for(var i = startRow; i <= endRow; i++){
22845             this.deselectRow(i, preventViewNotify);
22846         }
22847     },
22848
22849     /**
22850      * Selects a row.
22851      * @param {Number} row The index of the row to select
22852      * @param {Boolean} keepExisting (optional) True to keep existing selections
22853      */
22854     selectRow : function(index, keepExisting, preventViewNotify)
22855     {
22856             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22857             return;
22858         }
22859         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22860             if(!keepExisting || this.singleSelect){
22861                 this.clearSelections();
22862             }
22863             
22864             var r = this.grid.store.getAt(index);
22865             //console.log('selectRow - record id :' + r.id);
22866             
22867             this.selections.add(r);
22868             this.last = this.lastActive = index;
22869             if(!preventViewNotify){
22870                 var proxy = new Roo.Element(
22871                                 this.grid.getRowDom(index)
22872                 );
22873                 proxy.addClass('bg-info info');
22874             }
22875             this.fireEvent("rowselect", this, index, r);
22876             this.fireEvent("selectionchange", this);
22877         }
22878     },
22879
22880     /**
22881      * Deselects a row.
22882      * @param {Number} row The index of the row to deselect
22883      */
22884     deselectRow : function(index, preventViewNotify)
22885     {
22886         if(this.locked) {
22887             return;
22888         }
22889         if(this.last == index){
22890             this.last = false;
22891         }
22892         if(this.lastActive == index){
22893             this.lastActive = false;
22894         }
22895         
22896         var r = this.grid.store.getAt(index);
22897         if (!r) {
22898             return;
22899         }
22900         
22901         this.selections.remove(r);
22902         //.console.log('deselectRow - record id :' + r.id);
22903         if(!preventViewNotify){
22904         
22905             var proxy = new Roo.Element(
22906                 this.grid.getRowDom(index)
22907             );
22908             proxy.removeClass('bg-info info');
22909         }
22910         this.fireEvent("rowdeselect", this, index);
22911         this.fireEvent("selectionchange", this);
22912     },
22913
22914     // private
22915     restoreLast : function(){
22916         if(this._last){
22917             this.last = this._last;
22918         }
22919     },
22920
22921     // private
22922     acceptsNav : function(row, col, cm){
22923         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22924     },
22925
22926     // private
22927     onEditorKey : function(field, e){
22928         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22929         if(k == e.TAB){
22930             e.stopEvent();
22931             ed.completeEdit();
22932             if(e.shiftKey){
22933                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22934             }else{
22935                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22936             }
22937         }else if(k == e.ENTER && !e.ctrlKey){
22938             e.stopEvent();
22939             ed.completeEdit();
22940             if(e.shiftKey){
22941                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22942             }else{
22943                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22944             }
22945         }else if(k == e.ESC){
22946             ed.cancelEdit();
22947         }
22948         if(newCell){
22949             g.startEditing(newCell[0], newCell[1]);
22950         }
22951     }
22952 });
22953 /*
22954  * Based on:
22955  * Ext JS Library 1.1.1
22956  * Copyright(c) 2006-2007, Ext JS, LLC.
22957  *
22958  * Originally Released Under LGPL - original licence link has changed is not relivant.
22959  *
22960  * Fork - LGPL
22961  * <script type="text/javascript">
22962  */
22963  
22964 /**
22965  * @class Roo.bootstrap.PagingToolbar
22966  * @extends Roo.bootstrap.NavSimplebar
22967  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22968  * @constructor
22969  * Create a new PagingToolbar
22970  * @param {Object} config The config object
22971  * @param {Roo.data.Store} store
22972  */
22973 Roo.bootstrap.PagingToolbar = function(config)
22974 {
22975     // old args format still supported... - xtype is prefered..
22976         // created from xtype...
22977     
22978     this.ds = config.dataSource;
22979     
22980     if (config.store && !this.ds) {
22981         this.store= Roo.factory(config.store, Roo.data);
22982         this.ds = this.store;
22983         this.ds.xmodule = this.xmodule || false;
22984     }
22985     
22986     this.toolbarItems = [];
22987     if (config.items) {
22988         this.toolbarItems = config.items;
22989     }
22990     
22991     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22992     
22993     this.cursor = 0;
22994     
22995     if (this.ds) { 
22996         this.bind(this.ds);
22997     }
22998     
22999     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23000     
23001 };
23002
23003 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23004     /**
23005      * @cfg {Roo.data.Store} dataSource
23006      * The underlying data store providing the paged data
23007      */
23008     /**
23009      * @cfg {String/HTMLElement/Element} container
23010      * container The id or element that will contain the toolbar
23011      */
23012     /**
23013      * @cfg {Boolean} displayInfo
23014      * True to display the displayMsg (defaults to false)
23015      */
23016     /**
23017      * @cfg {Number} pageSize
23018      * The number of records to display per page (defaults to 20)
23019      */
23020     pageSize: 20,
23021     /**
23022      * @cfg {String} displayMsg
23023      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23024      */
23025     displayMsg : 'Displaying {0} - {1} of {2}',
23026     /**
23027      * @cfg {String} emptyMsg
23028      * The message to display when no records are found (defaults to "No data to display")
23029      */
23030     emptyMsg : 'No data to display',
23031     /**
23032      * Customizable piece of the default paging text (defaults to "Page")
23033      * @type String
23034      */
23035     beforePageText : "Page",
23036     /**
23037      * Customizable piece of the default paging text (defaults to "of %0")
23038      * @type String
23039      */
23040     afterPageText : "of {0}",
23041     /**
23042      * Customizable piece of the default paging text (defaults to "First Page")
23043      * @type String
23044      */
23045     firstText : "First Page",
23046     /**
23047      * Customizable piece of the default paging text (defaults to "Previous Page")
23048      * @type String
23049      */
23050     prevText : "Previous Page",
23051     /**
23052      * Customizable piece of the default paging text (defaults to "Next Page")
23053      * @type String
23054      */
23055     nextText : "Next Page",
23056     /**
23057      * Customizable piece of the default paging text (defaults to "Last Page")
23058      * @type String
23059      */
23060     lastText : "Last Page",
23061     /**
23062      * Customizable piece of the default paging text (defaults to "Refresh")
23063      * @type String
23064      */
23065     refreshText : "Refresh",
23066
23067     buttons : false,
23068     // private
23069     onRender : function(ct, position) 
23070     {
23071         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23072         this.navgroup.parentId = this.id;
23073         this.navgroup.onRender(this.el, null);
23074         // add the buttons to the navgroup
23075         
23076         if(this.displayInfo){
23077             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23078             this.displayEl = this.el.select('.x-paging-info', true).first();
23079 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23080 //            this.displayEl = navel.el.select('span',true).first();
23081         }
23082         
23083         var _this = this;
23084         
23085         if(this.buttons){
23086             Roo.each(_this.buttons, function(e){ // this might need to use render????
23087                Roo.factory(e).onRender(_this.el, null);
23088             });
23089         }
23090             
23091         Roo.each(_this.toolbarItems, function(e) {
23092             _this.navgroup.addItem(e);
23093         });
23094         
23095         
23096         this.first = this.navgroup.addItem({
23097             tooltip: this.firstText,
23098             cls: "prev",
23099             icon : 'fa fa-backward',
23100             disabled: true,
23101             preventDefault: true,
23102             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23103         });
23104         
23105         this.prev =  this.navgroup.addItem({
23106             tooltip: this.prevText,
23107             cls: "prev",
23108             icon : 'fa fa-step-backward',
23109             disabled: true,
23110             preventDefault: true,
23111             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23112         });
23113     //this.addSeparator();
23114         
23115         
23116         var field = this.navgroup.addItem( {
23117             tagtype : 'span',
23118             cls : 'x-paging-position',
23119             
23120             html : this.beforePageText  +
23121                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23122                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23123          } ); //?? escaped?
23124         
23125         this.field = field.el.select('input', true).first();
23126         this.field.on("keydown", this.onPagingKeydown, this);
23127         this.field.on("focus", function(){this.dom.select();});
23128     
23129     
23130         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23131         //this.field.setHeight(18);
23132         //this.addSeparator();
23133         this.next = this.navgroup.addItem({
23134             tooltip: this.nextText,
23135             cls: "next",
23136             html : ' <i class="fa fa-step-forward">',
23137             disabled: true,
23138             preventDefault: true,
23139             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23140         });
23141         this.last = this.navgroup.addItem({
23142             tooltip: this.lastText,
23143             icon : 'fa fa-forward',
23144             cls: "next",
23145             disabled: true,
23146             preventDefault: true,
23147             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23148         });
23149     //this.addSeparator();
23150         this.loading = this.navgroup.addItem({
23151             tooltip: this.refreshText,
23152             icon: 'fa fa-refresh',
23153             preventDefault: true,
23154             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23155         });
23156         
23157     },
23158
23159     // private
23160     updateInfo : function(){
23161         if(this.displayEl){
23162             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23163             var msg = count == 0 ?
23164                 this.emptyMsg :
23165                 String.format(
23166                     this.displayMsg,
23167                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23168                 );
23169             this.displayEl.update(msg);
23170         }
23171     },
23172
23173     // private
23174     onLoad : function(ds, r, o){
23175        this.cursor = o.params ? o.params.start : 0;
23176        var d = this.getPageData(),
23177             ap = d.activePage,
23178             ps = d.pages;
23179         
23180        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23181        this.field.dom.value = ap;
23182        this.first.setDisabled(ap == 1);
23183        this.prev.setDisabled(ap == 1);
23184        this.next.setDisabled(ap == ps);
23185        this.last.setDisabled(ap == ps);
23186        this.loading.enable();
23187        this.updateInfo();
23188     },
23189
23190     // private
23191     getPageData : function(){
23192         var total = this.ds.getTotalCount();
23193         return {
23194             total : total,
23195             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23196             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23197         };
23198     },
23199
23200     // private
23201     onLoadError : function(){
23202         this.loading.enable();
23203     },
23204
23205     // private
23206     onPagingKeydown : function(e){
23207         var k = e.getKey();
23208         var d = this.getPageData();
23209         if(k == e.RETURN){
23210             var v = this.field.dom.value, pageNum;
23211             if(!v || isNaN(pageNum = parseInt(v, 10))){
23212                 this.field.dom.value = d.activePage;
23213                 return;
23214             }
23215             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23216             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23217             e.stopEvent();
23218         }
23219         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))
23220         {
23221           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23222           this.field.dom.value = pageNum;
23223           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23224           e.stopEvent();
23225         }
23226         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23227         {
23228           var v = this.field.dom.value, pageNum; 
23229           var increment = (e.shiftKey) ? 10 : 1;
23230           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23231                 increment *= -1;
23232           }
23233           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23234             this.field.dom.value = d.activePage;
23235             return;
23236           }
23237           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23238           {
23239             this.field.dom.value = parseInt(v, 10) + increment;
23240             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23241             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23242           }
23243           e.stopEvent();
23244         }
23245     },
23246
23247     // private
23248     beforeLoad : function(){
23249         if(this.loading){
23250             this.loading.disable();
23251         }
23252     },
23253
23254     // private
23255     onClick : function(which){
23256         
23257         var ds = this.ds;
23258         if (!ds) {
23259             return;
23260         }
23261         
23262         switch(which){
23263             case "first":
23264                 ds.load({params:{start: 0, limit: this.pageSize}});
23265             break;
23266             case "prev":
23267                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23268             break;
23269             case "next":
23270                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23271             break;
23272             case "last":
23273                 var total = ds.getTotalCount();
23274                 var extra = total % this.pageSize;
23275                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23276                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23277             break;
23278             case "refresh":
23279                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23280             break;
23281         }
23282     },
23283
23284     /**
23285      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23286      * @param {Roo.data.Store} store The data store to unbind
23287      */
23288     unbind : function(ds){
23289         ds.un("beforeload", this.beforeLoad, this);
23290         ds.un("load", this.onLoad, this);
23291         ds.un("loadexception", this.onLoadError, this);
23292         ds.un("remove", this.updateInfo, this);
23293         ds.un("add", this.updateInfo, this);
23294         this.ds = undefined;
23295     },
23296
23297     /**
23298      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23299      * @param {Roo.data.Store} store The data store to bind
23300      */
23301     bind : function(ds){
23302         ds.on("beforeload", this.beforeLoad, this);
23303         ds.on("load", this.onLoad, this);
23304         ds.on("loadexception", this.onLoadError, this);
23305         ds.on("remove", this.updateInfo, this);
23306         ds.on("add", this.updateInfo, this);
23307         this.ds = ds;
23308     }
23309 });/*
23310  * - LGPL
23311  *
23312  * element
23313  * 
23314  */
23315
23316 /**
23317  * @class Roo.bootstrap.MessageBar
23318  * @extends Roo.bootstrap.Component
23319  * Bootstrap MessageBar class
23320  * @cfg {String} html contents of the MessageBar
23321  * @cfg {String} weight (info | success | warning | danger) default info
23322  * @cfg {String} beforeClass insert the bar before the given class
23323  * @cfg {Boolean} closable (true | false) default false
23324  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23325  * 
23326  * @constructor
23327  * Create a new Element
23328  * @param {Object} config The config object
23329  */
23330
23331 Roo.bootstrap.MessageBar = function(config){
23332     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23333 };
23334
23335 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23336     
23337     html: '',
23338     weight: 'info',
23339     closable: false,
23340     fixed: false,
23341     beforeClass: 'bootstrap-sticky-wrap',
23342     
23343     getAutoCreate : function(){
23344         
23345         var cfg = {
23346             tag: 'div',
23347             cls: 'alert alert-dismissable alert-' + this.weight,
23348             cn: [
23349                 {
23350                     tag: 'span',
23351                     cls: 'message',
23352                     html: this.html || ''
23353                 }
23354             ]
23355         };
23356         
23357         if(this.fixed){
23358             cfg.cls += ' alert-messages-fixed';
23359         }
23360         
23361         if(this.closable){
23362             cfg.cn.push({
23363                 tag: 'button',
23364                 cls: 'close',
23365                 html: 'x'
23366             });
23367         }
23368         
23369         return cfg;
23370     },
23371     
23372     onRender : function(ct, position)
23373     {
23374         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23375         
23376         if(!this.el){
23377             var cfg = Roo.apply({},  this.getAutoCreate());
23378             cfg.id = Roo.id();
23379             
23380             if (this.cls) {
23381                 cfg.cls += ' ' + this.cls;
23382             }
23383             if (this.style) {
23384                 cfg.style = this.style;
23385             }
23386             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23387             
23388             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23389         }
23390         
23391         this.el.select('>button.close').on('click', this.hide, this);
23392         
23393     },
23394     
23395     show : function()
23396     {
23397         if (!this.rendered) {
23398             this.render();
23399         }
23400         
23401         this.el.show();
23402         
23403         this.fireEvent('show', this);
23404         
23405     },
23406     
23407     hide : function()
23408     {
23409         if (!this.rendered) {
23410             this.render();
23411         }
23412         
23413         this.el.hide();
23414         
23415         this.fireEvent('hide', this);
23416     },
23417     
23418     update : function()
23419     {
23420 //        var e = this.el.dom.firstChild;
23421 //        
23422 //        if(this.closable){
23423 //            e = e.nextSibling;
23424 //        }
23425 //        
23426 //        e.data = this.html || '';
23427
23428         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23429     }
23430    
23431 });
23432
23433  
23434
23435      /*
23436  * - LGPL
23437  *
23438  * Graph
23439  * 
23440  */
23441
23442
23443 /**
23444  * @class Roo.bootstrap.Graph
23445  * @extends Roo.bootstrap.Component
23446  * Bootstrap Graph class
23447 > Prameters
23448  -sm {number} sm 4
23449  -md {number} md 5
23450  @cfg {String} graphtype  bar | vbar | pie
23451  @cfg {number} g_x coodinator | centre x (pie)
23452  @cfg {number} g_y coodinator | centre y (pie)
23453  @cfg {number} g_r radius (pie)
23454  @cfg {number} g_height height of the chart (respected by all elements in the set)
23455  @cfg {number} g_width width of the chart (respected by all elements in the set)
23456  @cfg {Object} title The title of the chart
23457     
23458  -{Array}  values
23459  -opts (object) options for the chart 
23460      o {
23461      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23462      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23463      o vgutter (number)
23464      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.
23465      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23466      o to
23467      o stretch (boolean)
23468      o }
23469  -opts (object) options for the pie
23470      o{
23471      o cut
23472      o startAngle (number)
23473      o endAngle (number)
23474      } 
23475  *
23476  * @constructor
23477  * Create a new Input
23478  * @param {Object} config The config object
23479  */
23480
23481 Roo.bootstrap.Graph = function(config){
23482     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23483     
23484     this.addEvents({
23485         // img events
23486         /**
23487          * @event click
23488          * The img click event for the img.
23489          * @param {Roo.EventObject} e
23490          */
23491         "click" : true
23492     });
23493 };
23494
23495 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23496     
23497     sm: 4,
23498     md: 5,
23499     graphtype: 'bar',
23500     g_height: 250,
23501     g_width: 400,
23502     g_x: 50,
23503     g_y: 50,
23504     g_r: 30,
23505     opts:{
23506         //g_colors: this.colors,
23507         g_type: 'soft',
23508         g_gutter: '20%'
23509
23510     },
23511     title : false,
23512
23513     getAutoCreate : function(){
23514         
23515         var cfg = {
23516             tag: 'div',
23517             html : null
23518         };
23519         
23520         
23521         return  cfg;
23522     },
23523
23524     onRender : function(ct,position){
23525         
23526         
23527         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23528         
23529         if (typeof(Raphael) == 'undefined') {
23530             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23531             return;
23532         }
23533         
23534         this.raphael = Raphael(this.el.dom);
23535         
23536                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23537                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23538                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23539                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23540                 /*
23541                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23542                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23543                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23544                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23545                 
23546                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23547                 r.barchart(330, 10, 300, 220, data1);
23548                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23549                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23550                 */
23551                 
23552                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23553                 // r.barchart(30, 30, 560, 250,  xdata, {
23554                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23555                 //     axis : "0 0 1 1",
23556                 //     axisxlabels :  xdata
23557                 //     //yvalues : cols,
23558                    
23559                 // });
23560 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23561 //        
23562 //        this.load(null,xdata,{
23563 //                axis : "0 0 1 1",
23564 //                axisxlabels :  xdata
23565 //                });
23566
23567     },
23568
23569     load : function(graphtype,xdata,opts)
23570     {
23571         this.raphael.clear();
23572         if(!graphtype) {
23573             graphtype = this.graphtype;
23574         }
23575         if(!opts){
23576             opts = this.opts;
23577         }
23578         var r = this.raphael,
23579             fin = function () {
23580                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23581             },
23582             fout = function () {
23583                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23584             },
23585             pfin = function() {
23586                 this.sector.stop();
23587                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23588
23589                 if (this.label) {
23590                     this.label[0].stop();
23591                     this.label[0].attr({ r: 7.5 });
23592                     this.label[1].attr({ "font-weight": 800 });
23593                 }
23594             },
23595             pfout = function() {
23596                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23597
23598                 if (this.label) {
23599                     this.label[0].animate({ r: 5 }, 500, "bounce");
23600                     this.label[1].attr({ "font-weight": 400 });
23601                 }
23602             };
23603
23604         switch(graphtype){
23605             case 'bar':
23606                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23607                 break;
23608             case 'hbar':
23609                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23610                 break;
23611             case 'pie':
23612 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23613 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23614 //            
23615                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23616                 
23617                 break;
23618
23619         }
23620         
23621         if(this.title){
23622             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23623         }
23624         
23625     },
23626     
23627     setTitle: function(o)
23628     {
23629         this.title = o;
23630     },
23631     
23632     initEvents: function() {
23633         
23634         if(!this.href){
23635             this.el.on('click', this.onClick, this);
23636         }
23637     },
23638     
23639     onClick : function(e)
23640     {
23641         Roo.log('img onclick');
23642         this.fireEvent('click', this, e);
23643     }
23644    
23645 });
23646
23647  
23648 /*
23649  * - LGPL
23650  *
23651  * numberBox
23652  * 
23653  */
23654 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23655
23656 /**
23657  * @class Roo.bootstrap.dash.NumberBox
23658  * @extends Roo.bootstrap.Component
23659  * Bootstrap NumberBox class
23660  * @cfg {String} headline Box headline
23661  * @cfg {String} content Box content
23662  * @cfg {String} icon Box icon
23663  * @cfg {String} footer Footer text
23664  * @cfg {String} fhref Footer href
23665  * 
23666  * @constructor
23667  * Create a new NumberBox
23668  * @param {Object} config The config object
23669  */
23670
23671
23672 Roo.bootstrap.dash.NumberBox = function(config){
23673     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23674     
23675 };
23676
23677 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23678     
23679     headline : '',
23680     content : '',
23681     icon : '',
23682     footer : '',
23683     fhref : '',
23684     ficon : '',
23685     
23686     getAutoCreate : function(){
23687         
23688         var cfg = {
23689             tag : 'div',
23690             cls : 'small-box ',
23691             cn : [
23692                 {
23693                     tag : 'div',
23694                     cls : 'inner',
23695                     cn :[
23696                         {
23697                             tag : 'h3',
23698                             cls : 'roo-headline',
23699                             html : this.headline
23700                         },
23701                         {
23702                             tag : 'p',
23703                             cls : 'roo-content',
23704                             html : this.content
23705                         }
23706                     ]
23707                 }
23708             ]
23709         };
23710         
23711         if(this.icon){
23712             cfg.cn.push({
23713                 tag : 'div',
23714                 cls : 'icon',
23715                 cn :[
23716                     {
23717                         tag : 'i',
23718                         cls : 'ion ' + this.icon
23719                     }
23720                 ]
23721             });
23722         }
23723         
23724         if(this.footer){
23725             var footer = {
23726                 tag : 'a',
23727                 cls : 'small-box-footer',
23728                 href : this.fhref || '#',
23729                 html : this.footer
23730             };
23731             
23732             cfg.cn.push(footer);
23733             
23734         }
23735         
23736         return  cfg;
23737     },
23738
23739     onRender : function(ct,position){
23740         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23741
23742
23743        
23744                 
23745     },
23746
23747     setHeadline: function (value)
23748     {
23749         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23750     },
23751     
23752     setFooter: function (value, href)
23753     {
23754         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23755         
23756         if(href){
23757             this.el.select('a.small-box-footer',true).first().attr('href', href);
23758         }
23759         
23760     },
23761
23762     setContent: function (value)
23763     {
23764         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23765     },
23766
23767     initEvents: function() 
23768     {   
23769         
23770     }
23771     
23772 });
23773
23774  
23775 /*
23776  * - LGPL
23777  *
23778  * TabBox
23779  * 
23780  */
23781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23782
23783 /**
23784  * @class Roo.bootstrap.dash.TabBox
23785  * @extends Roo.bootstrap.Component
23786  * Bootstrap TabBox class
23787  * @cfg {String} title Title of the TabBox
23788  * @cfg {String} icon Icon of the TabBox
23789  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23790  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23791  * 
23792  * @constructor
23793  * Create a new TabBox
23794  * @param {Object} config The config object
23795  */
23796
23797
23798 Roo.bootstrap.dash.TabBox = function(config){
23799     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23800     this.addEvents({
23801         // raw events
23802         /**
23803          * @event addpane
23804          * When a pane is added
23805          * @param {Roo.bootstrap.dash.TabPane} pane
23806          */
23807         "addpane" : true,
23808         /**
23809          * @event activatepane
23810          * When a pane is activated
23811          * @param {Roo.bootstrap.dash.TabPane} pane
23812          */
23813         "activatepane" : true
23814         
23815          
23816     });
23817     
23818     this.panes = [];
23819 };
23820
23821 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23822
23823     title : '',
23824     icon : false,
23825     showtabs : true,
23826     tabScrollable : false,
23827     
23828     getChildContainer : function()
23829     {
23830         return this.el.select('.tab-content', true).first();
23831     },
23832     
23833     getAutoCreate : function(){
23834         
23835         var header = {
23836             tag: 'li',
23837             cls: 'pull-left header',
23838             html: this.title,
23839             cn : []
23840         };
23841         
23842         if(this.icon){
23843             header.cn.push({
23844                 tag: 'i',
23845                 cls: 'fa ' + this.icon
23846             });
23847         }
23848         
23849         var h = {
23850             tag: 'ul',
23851             cls: 'nav nav-tabs pull-right',
23852             cn: [
23853                 header
23854             ]
23855         };
23856         
23857         if(this.tabScrollable){
23858             h = {
23859                 tag: 'div',
23860                 cls: 'tab-header',
23861                 cn: [
23862                     {
23863                         tag: 'ul',
23864                         cls: 'nav nav-tabs pull-right',
23865                         cn: [
23866                             header
23867                         ]
23868                     }
23869                 ]
23870             };
23871         }
23872         
23873         var cfg = {
23874             tag: 'div',
23875             cls: 'nav-tabs-custom',
23876             cn: [
23877                 h,
23878                 {
23879                     tag: 'div',
23880                     cls: 'tab-content no-padding',
23881                     cn: []
23882                 }
23883             ]
23884         };
23885
23886         return  cfg;
23887     },
23888     initEvents : function()
23889     {
23890         //Roo.log('add add pane handler');
23891         this.on('addpane', this.onAddPane, this);
23892     },
23893      /**
23894      * Updates the box title
23895      * @param {String} html to set the title to.
23896      */
23897     setTitle : function(value)
23898     {
23899         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23900     },
23901     onAddPane : function(pane)
23902     {
23903         this.panes.push(pane);
23904         //Roo.log('addpane');
23905         //Roo.log(pane);
23906         // tabs are rendere left to right..
23907         if(!this.showtabs){
23908             return;
23909         }
23910         
23911         var ctr = this.el.select('.nav-tabs', true).first();
23912          
23913          
23914         var existing = ctr.select('.nav-tab',true);
23915         var qty = existing.getCount();;
23916         
23917         
23918         var tab = ctr.createChild({
23919             tag : 'li',
23920             cls : 'nav-tab' + (qty ? '' : ' active'),
23921             cn : [
23922                 {
23923                     tag : 'a',
23924                     href:'#',
23925                     html : pane.title
23926                 }
23927             ]
23928         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23929         pane.tab = tab;
23930         
23931         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23932         if (!qty) {
23933             pane.el.addClass('active');
23934         }
23935         
23936                 
23937     },
23938     onTabClick : function(ev,un,ob,pane)
23939     {
23940         //Roo.log('tab - prev default');
23941         ev.preventDefault();
23942         
23943         
23944         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23945         pane.tab.addClass('active');
23946         //Roo.log(pane.title);
23947         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23948         // technically we should have a deactivate event.. but maybe add later.
23949         // and it should not de-activate the selected tab...
23950         this.fireEvent('activatepane', pane);
23951         pane.el.addClass('active');
23952         pane.fireEvent('activate');
23953         
23954         
23955     },
23956     
23957     getActivePane : function()
23958     {
23959         var r = false;
23960         Roo.each(this.panes, function(p) {
23961             if(p.el.hasClass('active')){
23962                 r = p;
23963                 return false;
23964             }
23965             
23966             return;
23967         });
23968         
23969         return r;
23970     }
23971     
23972     
23973 });
23974
23975  
23976 /*
23977  * - LGPL
23978  *
23979  * Tab pane
23980  * 
23981  */
23982 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23983 /**
23984  * @class Roo.bootstrap.TabPane
23985  * @extends Roo.bootstrap.Component
23986  * Bootstrap TabPane class
23987  * @cfg {Boolean} active (false | true) Default false
23988  * @cfg {String} title title of panel
23989
23990  * 
23991  * @constructor
23992  * Create a new TabPane
23993  * @param {Object} config The config object
23994  */
23995
23996 Roo.bootstrap.dash.TabPane = function(config){
23997     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23998     
23999     this.addEvents({
24000         // raw events
24001         /**
24002          * @event activate
24003          * When a pane is activated
24004          * @param {Roo.bootstrap.dash.TabPane} pane
24005          */
24006         "activate" : true
24007          
24008     });
24009 };
24010
24011 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24012     
24013     active : false,
24014     title : '',
24015     
24016     // the tabBox that this is attached to.
24017     tab : false,
24018      
24019     getAutoCreate : function() 
24020     {
24021         var cfg = {
24022             tag: 'div',
24023             cls: 'tab-pane'
24024         };
24025         
24026         if(this.active){
24027             cfg.cls += ' active';
24028         }
24029         
24030         return cfg;
24031     },
24032     initEvents  : function()
24033     {
24034         //Roo.log('trigger add pane handler');
24035         this.parent().fireEvent('addpane', this)
24036     },
24037     
24038      /**
24039      * Updates the tab title 
24040      * @param {String} html to set the title to.
24041      */
24042     setTitle: function(str)
24043     {
24044         if (!this.tab) {
24045             return;
24046         }
24047         this.title = str;
24048         this.tab.select('a', true).first().dom.innerHTML = str;
24049         
24050     }
24051     
24052     
24053     
24054 });
24055
24056  
24057
24058
24059  /*
24060  * - LGPL
24061  *
24062  * menu
24063  * 
24064  */
24065 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24066
24067 /**
24068  * @class Roo.bootstrap.menu.Menu
24069  * @extends Roo.bootstrap.Component
24070  * Bootstrap Menu class - container for Menu
24071  * @cfg {String} html Text of the menu
24072  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24073  * @cfg {String} icon Font awesome icon
24074  * @cfg {String} pos Menu align to (top | bottom) default bottom
24075  * 
24076  * 
24077  * @constructor
24078  * Create a new Menu
24079  * @param {Object} config The config object
24080  */
24081
24082
24083 Roo.bootstrap.menu.Menu = function(config){
24084     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24085     
24086     this.addEvents({
24087         /**
24088          * @event beforeshow
24089          * Fires before this menu is displayed
24090          * @param {Roo.bootstrap.menu.Menu} this
24091          */
24092         beforeshow : true,
24093         /**
24094          * @event beforehide
24095          * Fires before this menu is hidden
24096          * @param {Roo.bootstrap.menu.Menu} this
24097          */
24098         beforehide : true,
24099         /**
24100          * @event show
24101          * Fires after this menu is displayed
24102          * @param {Roo.bootstrap.menu.Menu} this
24103          */
24104         show : true,
24105         /**
24106          * @event hide
24107          * Fires after this menu is hidden
24108          * @param {Roo.bootstrap.menu.Menu} this
24109          */
24110         hide : true,
24111         /**
24112          * @event click
24113          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24114          * @param {Roo.bootstrap.menu.Menu} this
24115          * @param {Roo.EventObject} e
24116          */
24117         click : true
24118     });
24119     
24120 };
24121
24122 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24123     
24124     submenu : false,
24125     html : '',
24126     weight : 'default',
24127     icon : false,
24128     pos : 'bottom',
24129     
24130     
24131     getChildContainer : function() {
24132         if(this.isSubMenu){
24133             return this.el;
24134         }
24135         
24136         return this.el.select('ul.dropdown-menu', true).first();  
24137     },
24138     
24139     getAutoCreate : function()
24140     {
24141         var text = [
24142             {
24143                 tag : 'span',
24144                 cls : 'roo-menu-text',
24145                 html : this.html
24146             }
24147         ];
24148         
24149         if(this.icon){
24150             text.unshift({
24151                 tag : 'i',
24152                 cls : 'fa ' + this.icon
24153             })
24154         }
24155         
24156         
24157         var cfg = {
24158             tag : 'div',
24159             cls : 'btn-group',
24160             cn : [
24161                 {
24162                     tag : 'button',
24163                     cls : 'dropdown-button btn btn-' + this.weight,
24164                     cn : text
24165                 },
24166                 {
24167                     tag : 'button',
24168                     cls : 'dropdown-toggle btn btn-' + this.weight,
24169                     cn : [
24170                         {
24171                             tag : 'span',
24172                             cls : 'caret'
24173                         }
24174                     ]
24175                 },
24176                 {
24177                     tag : 'ul',
24178                     cls : 'dropdown-menu'
24179                 }
24180             ]
24181             
24182         };
24183         
24184         if(this.pos == 'top'){
24185             cfg.cls += ' dropup';
24186         }
24187         
24188         if(this.isSubMenu){
24189             cfg = {
24190                 tag : 'ul',
24191                 cls : 'dropdown-menu'
24192             }
24193         }
24194         
24195         return cfg;
24196     },
24197     
24198     onRender : function(ct, position)
24199     {
24200         this.isSubMenu = ct.hasClass('dropdown-submenu');
24201         
24202         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24203     },
24204     
24205     initEvents : function() 
24206     {
24207         if(this.isSubMenu){
24208             return;
24209         }
24210         
24211         this.hidden = true;
24212         
24213         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24214         this.triggerEl.on('click', this.onTriggerPress, this);
24215         
24216         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24217         this.buttonEl.on('click', this.onClick, this);
24218         
24219     },
24220     
24221     list : function()
24222     {
24223         if(this.isSubMenu){
24224             return this.el;
24225         }
24226         
24227         return this.el.select('ul.dropdown-menu', true).first();
24228     },
24229     
24230     onClick : function(e)
24231     {
24232         this.fireEvent("click", this, e);
24233     },
24234     
24235     onTriggerPress  : function(e)
24236     {   
24237         if (this.isVisible()) {
24238             this.hide();
24239         } else {
24240             this.show();
24241         }
24242     },
24243     
24244     isVisible : function(){
24245         return !this.hidden;
24246     },
24247     
24248     show : function()
24249     {
24250         this.fireEvent("beforeshow", this);
24251         
24252         this.hidden = false;
24253         this.el.addClass('open');
24254         
24255         Roo.get(document).on("mouseup", this.onMouseUp, this);
24256         
24257         this.fireEvent("show", this);
24258         
24259         
24260     },
24261     
24262     hide : function()
24263     {
24264         this.fireEvent("beforehide", this);
24265         
24266         this.hidden = true;
24267         this.el.removeClass('open');
24268         
24269         Roo.get(document).un("mouseup", this.onMouseUp);
24270         
24271         this.fireEvent("hide", this);
24272     },
24273     
24274     onMouseUp : function()
24275     {
24276         this.hide();
24277     }
24278     
24279 });
24280
24281  
24282  /*
24283  * - LGPL
24284  *
24285  * menu item
24286  * 
24287  */
24288 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24289
24290 /**
24291  * @class Roo.bootstrap.menu.Item
24292  * @extends Roo.bootstrap.Component
24293  * Bootstrap MenuItem class
24294  * @cfg {Boolean} submenu (true | false) default false
24295  * @cfg {String} html text of the item
24296  * @cfg {String} href the link
24297  * @cfg {Boolean} disable (true | false) default false
24298  * @cfg {Boolean} preventDefault (true | false) default true
24299  * @cfg {String} icon Font awesome icon
24300  * @cfg {String} pos Submenu align to (left | right) default right 
24301  * 
24302  * 
24303  * @constructor
24304  * Create a new Item
24305  * @param {Object} config The config object
24306  */
24307
24308
24309 Roo.bootstrap.menu.Item = function(config){
24310     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24311     this.addEvents({
24312         /**
24313          * @event mouseover
24314          * Fires when the mouse is hovering over this menu
24315          * @param {Roo.bootstrap.menu.Item} this
24316          * @param {Roo.EventObject} e
24317          */
24318         mouseover : true,
24319         /**
24320          * @event mouseout
24321          * Fires when the mouse exits this menu
24322          * @param {Roo.bootstrap.menu.Item} this
24323          * @param {Roo.EventObject} e
24324          */
24325         mouseout : true,
24326         // raw events
24327         /**
24328          * @event click
24329          * The raw click event for the entire grid.
24330          * @param {Roo.EventObject} e
24331          */
24332         click : true
24333     });
24334 };
24335
24336 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24337     
24338     submenu : false,
24339     href : '',
24340     html : '',
24341     preventDefault: true,
24342     disable : false,
24343     icon : false,
24344     pos : 'right',
24345     
24346     getAutoCreate : function()
24347     {
24348         var text = [
24349             {
24350                 tag : 'span',
24351                 cls : 'roo-menu-item-text',
24352                 html : this.html
24353             }
24354         ];
24355         
24356         if(this.icon){
24357             text.unshift({
24358                 tag : 'i',
24359                 cls : 'fa ' + this.icon
24360             })
24361         }
24362         
24363         var cfg = {
24364             tag : 'li',
24365             cn : [
24366                 {
24367                     tag : 'a',
24368                     href : this.href || '#',
24369                     cn : text
24370                 }
24371             ]
24372         };
24373         
24374         if(this.disable){
24375             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24376         }
24377         
24378         if(this.submenu){
24379             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24380             
24381             if(this.pos == 'left'){
24382                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24383             }
24384         }
24385         
24386         return cfg;
24387     },
24388     
24389     initEvents : function() 
24390     {
24391         this.el.on('mouseover', this.onMouseOver, this);
24392         this.el.on('mouseout', this.onMouseOut, this);
24393         
24394         this.el.select('a', true).first().on('click', this.onClick, this);
24395         
24396     },
24397     
24398     onClick : function(e)
24399     {
24400         if(this.preventDefault){
24401             e.preventDefault();
24402         }
24403         
24404         this.fireEvent("click", this, e);
24405     },
24406     
24407     onMouseOver : function(e)
24408     {
24409         if(this.submenu && this.pos == 'left'){
24410             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24411         }
24412         
24413         this.fireEvent("mouseover", this, e);
24414     },
24415     
24416     onMouseOut : function(e)
24417     {
24418         this.fireEvent("mouseout", this, e);
24419     }
24420 });
24421
24422  
24423
24424  /*
24425  * - LGPL
24426  *
24427  * menu separator
24428  * 
24429  */
24430 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24431
24432 /**
24433  * @class Roo.bootstrap.menu.Separator
24434  * @extends Roo.bootstrap.Component
24435  * Bootstrap Separator class
24436  * 
24437  * @constructor
24438  * Create a new Separator
24439  * @param {Object} config The config object
24440  */
24441
24442
24443 Roo.bootstrap.menu.Separator = function(config){
24444     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24445 };
24446
24447 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24448     
24449     getAutoCreate : function(){
24450         var cfg = {
24451             tag : 'li',
24452             cls: 'divider'
24453         };
24454         
24455         return cfg;
24456     }
24457    
24458 });
24459
24460  
24461
24462  /*
24463  * - LGPL
24464  *
24465  * Tooltip
24466  * 
24467  */
24468
24469 /**
24470  * @class Roo.bootstrap.Tooltip
24471  * Bootstrap Tooltip class
24472  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24473  * to determine which dom element triggers the tooltip.
24474  * 
24475  * It needs to add support for additional attributes like tooltip-position
24476  * 
24477  * @constructor
24478  * Create a new Toolti
24479  * @param {Object} config The config object
24480  */
24481
24482 Roo.bootstrap.Tooltip = function(config){
24483     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24484 };
24485
24486 Roo.apply(Roo.bootstrap.Tooltip, {
24487     /**
24488      * @function init initialize tooltip monitoring.
24489      * @static
24490      */
24491     currentEl : false,
24492     currentTip : false,
24493     currentRegion : false,
24494     
24495     //  init : delay?
24496     
24497     init : function()
24498     {
24499         Roo.get(document).on('mouseover', this.enter ,this);
24500         Roo.get(document).on('mouseout', this.leave, this);
24501          
24502         
24503         this.currentTip = new Roo.bootstrap.Tooltip();
24504     },
24505     
24506     enter : function(ev)
24507     {
24508         var dom = ev.getTarget();
24509         
24510         //Roo.log(['enter',dom]);
24511         var el = Roo.fly(dom);
24512         if (this.currentEl) {
24513             //Roo.log(dom);
24514             //Roo.log(this.currentEl);
24515             //Roo.log(this.currentEl.contains(dom));
24516             if (this.currentEl == el) {
24517                 return;
24518             }
24519             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24520                 return;
24521             }
24522
24523         }
24524         
24525         if (this.currentTip.el) {
24526             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24527         }    
24528         //Roo.log(ev);
24529         
24530         if(!el || el.dom == document){
24531             return;
24532         }
24533         
24534         var bindEl = el;
24535         
24536         // you can not look for children, as if el is the body.. then everythign is the child..
24537         if (!el.attr('tooltip')) { //
24538             if (!el.select("[tooltip]").elements.length) {
24539                 return;
24540             }
24541             // is the mouse over this child...?
24542             bindEl = el.select("[tooltip]").first();
24543             var xy = ev.getXY();
24544             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24545                 //Roo.log("not in region.");
24546                 return;
24547             }
24548             //Roo.log("child element over..");
24549             
24550         }
24551         this.currentEl = bindEl;
24552         this.currentTip.bind(bindEl);
24553         this.currentRegion = Roo.lib.Region.getRegion(dom);
24554         this.currentTip.enter();
24555         
24556     },
24557     leave : function(ev)
24558     {
24559         var dom = ev.getTarget();
24560         //Roo.log(['leave',dom]);
24561         if (!this.currentEl) {
24562             return;
24563         }
24564         
24565         
24566         if (dom != this.currentEl.dom) {
24567             return;
24568         }
24569         var xy = ev.getXY();
24570         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24571             return;
24572         }
24573         // only activate leave if mouse cursor is outside... bounding box..
24574         
24575         
24576         
24577         
24578         if (this.currentTip) {
24579             this.currentTip.leave();
24580         }
24581         //Roo.log('clear currentEl');
24582         this.currentEl = false;
24583         
24584         
24585     },
24586     alignment : {
24587         'left' : ['r-l', [-2,0], 'right'],
24588         'right' : ['l-r', [2,0], 'left'],
24589         'bottom' : ['t-b', [0,2], 'top'],
24590         'top' : [ 'b-t', [0,-2], 'bottom']
24591     }
24592     
24593 });
24594
24595
24596 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24597     
24598     
24599     bindEl : false,
24600     
24601     delay : null, // can be { show : 300 , hide: 500}
24602     
24603     timeout : null,
24604     
24605     hoverState : null, //???
24606     
24607     placement : 'bottom', 
24608     
24609     getAutoCreate : function(){
24610     
24611         var cfg = {
24612            cls : 'tooltip',
24613            role : 'tooltip',
24614            cn : [
24615                 {
24616                     cls : 'tooltip-arrow'
24617                 },
24618                 {
24619                     cls : 'tooltip-inner'
24620                 }
24621            ]
24622         };
24623         
24624         return cfg;
24625     },
24626     bind : function(el)
24627     {
24628         this.bindEl = el;
24629     },
24630       
24631     
24632     enter : function () {
24633        
24634         if (this.timeout != null) {
24635             clearTimeout(this.timeout);
24636         }
24637         
24638         this.hoverState = 'in';
24639          //Roo.log("enter - show");
24640         if (!this.delay || !this.delay.show) {
24641             this.show();
24642             return;
24643         }
24644         var _t = this;
24645         this.timeout = setTimeout(function () {
24646             if (_t.hoverState == 'in') {
24647                 _t.show();
24648             }
24649         }, this.delay.show);
24650     },
24651     leave : function()
24652     {
24653         clearTimeout(this.timeout);
24654     
24655         this.hoverState = 'out';
24656          if (!this.delay || !this.delay.hide) {
24657             this.hide();
24658             return;
24659         }
24660        
24661         var _t = this;
24662         this.timeout = setTimeout(function () {
24663             //Roo.log("leave - timeout");
24664             
24665             if (_t.hoverState == 'out') {
24666                 _t.hide();
24667                 Roo.bootstrap.Tooltip.currentEl = false;
24668             }
24669         }, delay);
24670     },
24671     
24672     show : function ()
24673     {
24674         if (!this.el) {
24675             this.render(document.body);
24676         }
24677         // set content.
24678         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24679         
24680         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24681         
24682         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24683         
24684         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24685         
24686         var placement = typeof this.placement == 'function' ?
24687             this.placement.call(this, this.el, on_el) :
24688             this.placement;
24689             
24690         var autoToken = /\s?auto?\s?/i;
24691         var autoPlace = autoToken.test(placement);
24692         if (autoPlace) {
24693             placement = placement.replace(autoToken, '') || 'top';
24694         }
24695         
24696         //this.el.detach()
24697         //this.el.setXY([0,0]);
24698         this.el.show();
24699         //this.el.dom.style.display='block';
24700         
24701         //this.el.appendTo(on_el);
24702         
24703         var p = this.getPosition();
24704         var box = this.el.getBox();
24705         
24706         if (autoPlace) {
24707             // fixme..
24708         }
24709         
24710         var align = Roo.bootstrap.Tooltip.alignment[placement];
24711         
24712         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24713         
24714         if(placement == 'top' || placement == 'bottom'){
24715             if(xy[0] < 0){
24716                 placement = 'right';
24717             }
24718             
24719             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24720                 placement = 'left';
24721             }
24722             
24723             var scroll = Roo.select('body', true).first().getScroll();
24724             
24725             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24726                 placement = 'top';
24727             }
24728             
24729         }
24730         
24731         align = Roo.bootstrap.Tooltip.alignment[placement];
24732         
24733         this.el.alignTo(this.bindEl, align[0],align[1]);
24734         //var arrow = this.el.select('.arrow',true).first();
24735         //arrow.set(align[2], 
24736         
24737         this.el.addClass(placement);
24738         
24739         this.el.addClass('in fade');
24740         
24741         this.hoverState = null;
24742         
24743         if (this.el.hasClass('fade')) {
24744             // fade it?
24745         }
24746         
24747     },
24748     hide : function()
24749     {
24750          
24751         if (!this.el) {
24752             return;
24753         }
24754         //this.el.setXY([0,0]);
24755         this.el.removeClass('in');
24756         //this.el.hide();
24757         
24758     }
24759     
24760 });
24761  
24762
24763  /*
24764  * - LGPL
24765  *
24766  * Location Picker
24767  * 
24768  */
24769
24770 /**
24771  * @class Roo.bootstrap.LocationPicker
24772  * @extends Roo.bootstrap.Component
24773  * Bootstrap LocationPicker class
24774  * @cfg {Number} latitude Position when init default 0
24775  * @cfg {Number} longitude Position when init default 0
24776  * @cfg {Number} zoom default 15
24777  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24778  * @cfg {Boolean} mapTypeControl default false
24779  * @cfg {Boolean} disableDoubleClickZoom default false
24780  * @cfg {Boolean} scrollwheel default true
24781  * @cfg {Boolean} streetViewControl default false
24782  * @cfg {Number} radius default 0
24783  * @cfg {String} locationName
24784  * @cfg {Boolean} draggable default true
24785  * @cfg {Boolean} enableAutocomplete default false
24786  * @cfg {Boolean} enableReverseGeocode default true
24787  * @cfg {String} markerTitle
24788  * 
24789  * @constructor
24790  * Create a new LocationPicker
24791  * @param {Object} config The config object
24792  */
24793
24794
24795 Roo.bootstrap.LocationPicker = function(config){
24796     
24797     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24798     
24799     this.addEvents({
24800         /**
24801          * @event initial
24802          * Fires when the picker initialized.
24803          * @param {Roo.bootstrap.LocationPicker} this
24804          * @param {Google Location} location
24805          */
24806         initial : true,
24807         /**
24808          * @event positionchanged
24809          * Fires when the picker position changed.
24810          * @param {Roo.bootstrap.LocationPicker} this
24811          * @param {Google Location} location
24812          */
24813         positionchanged : true,
24814         /**
24815          * @event resize
24816          * Fires when the map resize.
24817          * @param {Roo.bootstrap.LocationPicker} this
24818          */
24819         resize : true,
24820         /**
24821          * @event show
24822          * Fires when the map show.
24823          * @param {Roo.bootstrap.LocationPicker} this
24824          */
24825         show : true,
24826         /**
24827          * @event hide
24828          * Fires when the map hide.
24829          * @param {Roo.bootstrap.LocationPicker} this
24830          */
24831         hide : true,
24832         /**
24833          * @event mapClick
24834          * Fires when click the map.
24835          * @param {Roo.bootstrap.LocationPicker} this
24836          * @param {Map event} e
24837          */
24838         mapClick : true,
24839         /**
24840          * @event mapRightClick
24841          * Fires when right click the map.
24842          * @param {Roo.bootstrap.LocationPicker} this
24843          * @param {Map event} e
24844          */
24845         mapRightClick : true,
24846         /**
24847          * @event markerClick
24848          * Fires when click the marker.
24849          * @param {Roo.bootstrap.LocationPicker} this
24850          * @param {Map event} e
24851          */
24852         markerClick : true,
24853         /**
24854          * @event markerRightClick
24855          * Fires when right click the marker.
24856          * @param {Roo.bootstrap.LocationPicker} this
24857          * @param {Map event} e
24858          */
24859         markerRightClick : true,
24860         /**
24861          * @event OverlayViewDraw
24862          * Fires when OverlayView Draw
24863          * @param {Roo.bootstrap.LocationPicker} this
24864          */
24865         OverlayViewDraw : true,
24866         /**
24867          * @event OverlayViewOnAdd
24868          * Fires when OverlayView Draw
24869          * @param {Roo.bootstrap.LocationPicker} this
24870          */
24871         OverlayViewOnAdd : true,
24872         /**
24873          * @event OverlayViewOnRemove
24874          * Fires when OverlayView Draw
24875          * @param {Roo.bootstrap.LocationPicker} this
24876          */
24877         OverlayViewOnRemove : true,
24878         /**
24879          * @event OverlayViewShow
24880          * Fires when OverlayView Draw
24881          * @param {Roo.bootstrap.LocationPicker} this
24882          * @param {Pixel} cpx
24883          */
24884         OverlayViewShow : true,
24885         /**
24886          * @event OverlayViewHide
24887          * Fires when OverlayView Draw
24888          * @param {Roo.bootstrap.LocationPicker} this
24889          */
24890         OverlayViewHide : true,
24891         /**
24892          * @event loadexception
24893          * Fires when load google lib failed.
24894          * @param {Roo.bootstrap.LocationPicker} this
24895          */
24896         loadexception : true
24897     });
24898         
24899 };
24900
24901 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24902     
24903     gMapContext: false,
24904     
24905     latitude: 0,
24906     longitude: 0,
24907     zoom: 15,
24908     mapTypeId: false,
24909     mapTypeControl: false,
24910     disableDoubleClickZoom: false,
24911     scrollwheel: true,
24912     streetViewControl: false,
24913     radius: 0,
24914     locationName: '',
24915     draggable: true,
24916     enableAutocomplete: false,
24917     enableReverseGeocode: true,
24918     markerTitle: '',
24919     
24920     getAutoCreate: function()
24921     {
24922
24923         var cfg = {
24924             tag: 'div',
24925             cls: 'roo-location-picker'
24926         };
24927         
24928         return cfg
24929     },
24930     
24931     initEvents: function(ct, position)
24932     {       
24933         if(!this.el.getWidth() || this.isApplied()){
24934             return;
24935         }
24936         
24937         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24938         
24939         this.initial();
24940     },
24941     
24942     initial: function()
24943     {
24944         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24945             this.fireEvent('loadexception', this);
24946             return;
24947         }
24948         
24949         if(!this.mapTypeId){
24950             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24951         }
24952         
24953         this.gMapContext = this.GMapContext();
24954         
24955         this.initOverlayView();
24956         
24957         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24958         
24959         var _this = this;
24960                 
24961         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24962             _this.setPosition(_this.gMapContext.marker.position);
24963         });
24964         
24965         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24966             _this.fireEvent('mapClick', this, event);
24967             
24968         });
24969
24970         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24971             _this.fireEvent('mapRightClick', this, event);
24972             
24973         });
24974         
24975         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24976             _this.fireEvent('markerClick', this, event);
24977             
24978         });
24979
24980         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24981             _this.fireEvent('markerRightClick', this, event);
24982             
24983         });
24984         
24985         this.setPosition(this.gMapContext.location);
24986         
24987         this.fireEvent('initial', this, this.gMapContext.location);
24988     },
24989     
24990     initOverlayView: function()
24991     {
24992         var _this = this;
24993         
24994         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24995             
24996             draw: function()
24997             {
24998                 _this.fireEvent('OverlayViewDraw', _this);
24999             },
25000             
25001             onAdd: function()
25002             {
25003                 _this.fireEvent('OverlayViewOnAdd', _this);
25004             },
25005             
25006             onRemove: function()
25007             {
25008                 _this.fireEvent('OverlayViewOnRemove', _this);
25009             },
25010             
25011             show: function(cpx)
25012             {
25013                 _this.fireEvent('OverlayViewShow', _this, cpx);
25014             },
25015             
25016             hide: function()
25017             {
25018                 _this.fireEvent('OverlayViewHide', _this);
25019             }
25020             
25021         });
25022     },
25023     
25024     fromLatLngToContainerPixel: function(event)
25025     {
25026         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25027     },
25028     
25029     isApplied: function() 
25030     {
25031         return this.getGmapContext() == false ? false : true;
25032     },
25033     
25034     getGmapContext: function() 
25035     {
25036         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25037     },
25038     
25039     GMapContext: function() 
25040     {
25041         var position = new google.maps.LatLng(this.latitude, this.longitude);
25042         
25043         var _map = new google.maps.Map(this.el.dom, {
25044             center: position,
25045             zoom: this.zoom,
25046             mapTypeId: this.mapTypeId,
25047             mapTypeControl: this.mapTypeControl,
25048             disableDoubleClickZoom: this.disableDoubleClickZoom,
25049             scrollwheel: this.scrollwheel,
25050             streetViewControl: this.streetViewControl,
25051             locationName: this.locationName,
25052             draggable: this.draggable,
25053             enableAutocomplete: this.enableAutocomplete,
25054             enableReverseGeocode: this.enableReverseGeocode
25055         });
25056         
25057         var _marker = new google.maps.Marker({
25058             position: position,
25059             map: _map,
25060             title: this.markerTitle,
25061             draggable: this.draggable
25062         });
25063         
25064         return {
25065             map: _map,
25066             marker: _marker,
25067             circle: null,
25068             location: position,
25069             radius: this.radius,
25070             locationName: this.locationName,
25071             addressComponents: {
25072                 formatted_address: null,
25073                 addressLine1: null,
25074                 addressLine2: null,
25075                 streetName: null,
25076                 streetNumber: null,
25077                 city: null,
25078                 district: null,
25079                 state: null,
25080                 stateOrProvince: null
25081             },
25082             settings: this,
25083             domContainer: this.el.dom,
25084             geodecoder: new google.maps.Geocoder()
25085         };
25086     },
25087     
25088     drawCircle: function(center, radius, options) 
25089     {
25090         if (this.gMapContext.circle != null) {
25091             this.gMapContext.circle.setMap(null);
25092         }
25093         if (radius > 0) {
25094             radius *= 1;
25095             options = Roo.apply({}, options, {
25096                 strokeColor: "#0000FF",
25097                 strokeOpacity: .35,
25098                 strokeWeight: 2,
25099                 fillColor: "#0000FF",
25100                 fillOpacity: .2
25101             });
25102             
25103             options.map = this.gMapContext.map;
25104             options.radius = radius;
25105             options.center = center;
25106             this.gMapContext.circle = new google.maps.Circle(options);
25107             return this.gMapContext.circle;
25108         }
25109         
25110         return null;
25111     },
25112     
25113     setPosition: function(location) 
25114     {
25115         this.gMapContext.location = location;
25116         this.gMapContext.marker.setPosition(location);
25117         this.gMapContext.map.panTo(location);
25118         this.drawCircle(location, this.gMapContext.radius, {});
25119         
25120         var _this = this;
25121         
25122         if (this.gMapContext.settings.enableReverseGeocode) {
25123             this.gMapContext.geodecoder.geocode({
25124                 latLng: this.gMapContext.location
25125             }, function(results, status) {
25126                 
25127                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25128                     _this.gMapContext.locationName = results[0].formatted_address;
25129                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25130                     
25131                     _this.fireEvent('positionchanged', this, location);
25132                 }
25133             });
25134             
25135             return;
25136         }
25137         
25138         this.fireEvent('positionchanged', this, location);
25139     },
25140     
25141     resize: function()
25142     {
25143         google.maps.event.trigger(this.gMapContext.map, "resize");
25144         
25145         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25146         
25147         this.fireEvent('resize', this);
25148     },
25149     
25150     setPositionByLatLng: function(latitude, longitude)
25151     {
25152         this.setPosition(new google.maps.LatLng(latitude, longitude));
25153     },
25154     
25155     getCurrentPosition: function() 
25156     {
25157         return {
25158             latitude: this.gMapContext.location.lat(),
25159             longitude: this.gMapContext.location.lng()
25160         };
25161     },
25162     
25163     getAddressName: function() 
25164     {
25165         return this.gMapContext.locationName;
25166     },
25167     
25168     getAddressComponents: function() 
25169     {
25170         return this.gMapContext.addressComponents;
25171     },
25172     
25173     address_component_from_google_geocode: function(address_components) 
25174     {
25175         var result = {};
25176         
25177         for (var i = 0; i < address_components.length; i++) {
25178             var component = address_components[i];
25179             if (component.types.indexOf("postal_code") >= 0) {
25180                 result.postalCode = component.short_name;
25181             } else if (component.types.indexOf("street_number") >= 0) {
25182                 result.streetNumber = component.short_name;
25183             } else if (component.types.indexOf("route") >= 0) {
25184                 result.streetName = component.short_name;
25185             } else if (component.types.indexOf("neighborhood") >= 0) {
25186                 result.city = component.short_name;
25187             } else if (component.types.indexOf("locality") >= 0) {
25188                 result.city = component.short_name;
25189             } else if (component.types.indexOf("sublocality") >= 0) {
25190                 result.district = component.short_name;
25191             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25192                 result.stateOrProvince = component.short_name;
25193             } else if (component.types.indexOf("country") >= 0) {
25194                 result.country = component.short_name;
25195             }
25196         }
25197         
25198         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25199         result.addressLine2 = "";
25200         return result;
25201     },
25202     
25203     setZoomLevel: function(zoom)
25204     {
25205         this.gMapContext.map.setZoom(zoom);
25206     },
25207     
25208     show: function()
25209     {
25210         if(!this.el){
25211             return;
25212         }
25213         
25214         this.el.show();
25215         
25216         this.resize();
25217         
25218         this.fireEvent('show', this);
25219     },
25220     
25221     hide: function()
25222     {
25223         if(!this.el){
25224             return;
25225         }
25226         
25227         this.el.hide();
25228         
25229         this.fireEvent('hide', this);
25230     }
25231     
25232 });
25233
25234 Roo.apply(Roo.bootstrap.LocationPicker, {
25235     
25236     OverlayView : function(map, options)
25237     {
25238         options = options || {};
25239         
25240         this.setMap(map);
25241     }
25242     
25243     
25244 });/*
25245  * - LGPL
25246  *
25247  * Alert
25248  * 
25249  */
25250
25251 /**
25252  * @class Roo.bootstrap.Alert
25253  * @extends Roo.bootstrap.Component
25254  * Bootstrap Alert class
25255  * @cfg {String} title The title of alert
25256  * @cfg {String} html The content of alert
25257  * @cfg {String} weight (  success | info | warning | danger )
25258  * @cfg {String} faicon font-awesomeicon
25259  * 
25260  * @constructor
25261  * Create a new alert
25262  * @param {Object} config The config object
25263  */
25264
25265
25266 Roo.bootstrap.Alert = function(config){
25267     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25268     
25269 };
25270
25271 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25272     
25273     title: '',
25274     html: '',
25275     weight: false,
25276     faicon: false,
25277     
25278     getAutoCreate : function()
25279     {
25280         
25281         var cfg = {
25282             tag : 'div',
25283             cls : 'alert',
25284             cn : [
25285                 {
25286                     tag : 'i',
25287                     cls : 'roo-alert-icon'
25288                     
25289                 },
25290                 {
25291                     tag : 'b',
25292                     cls : 'roo-alert-title',
25293                     html : this.title
25294                 },
25295                 {
25296                     tag : 'span',
25297                     cls : 'roo-alert-text',
25298                     html : this.html
25299                 }
25300             ]
25301         };
25302         
25303         if(this.faicon){
25304             cfg.cn[0].cls += ' fa ' + this.faicon;
25305         }
25306         
25307         if(this.weight){
25308             cfg.cls += ' alert-' + this.weight;
25309         }
25310         
25311         return cfg;
25312     },
25313     
25314     initEvents: function() 
25315     {
25316         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25317     },
25318     
25319     setTitle : function(str)
25320     {
25321         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25322     },
25323     
25324     setText : function(str)
25325     {
25326         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25327     },
25328     
25329     setWeight : function(weight)
25330     {
25331         if(this.weight){
25332             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25333         }
25334         
25335         this.weight = weight;
25336         
25337         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25338     },
25339     
25340     setIcon : function(icon)
25341     {
25342         if(this.faicon){
25343             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25344         }
25345         
25346         this.faicon = icon;
25347         
25348         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25349     },
25350     
25351     hide: function() 
25352     {
25353         this.el.hide();   
25354     },
25355     
25356     show: function() 
25357     {  
25358         this.el.show();   
25359     }
25360     
25361 });
25362
25363  
25364 /*
25365 * Licence: LGPL
25366 */
25367
25368 /**
25369  * @class Roo.bootstrap.UploadCropbox
25370  * @extends Roo.bootstrap.Component
25371  * Bootstrap UploadCropbox class
25372  * @cfg {String} emptyText show when image has been loaded
25373  * @cfg {String} rotateNotify show when image too small to rotate
25374  * @cfg {Number} errorTimeout default 3000
25375  * @cfg {Number} minWidth default 300
25376  * @cfg {Number} minHeight default 300
25377  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25378  * @cfg {Boolean} isDocument (true|false) default false
25379  * @cfg {String} url action url
25380  * @cfg {String} paramName default 'imageUpload'
25381  * @cfg {String} method default POST
25382  * @cfg {Boolean} loadMask (true|false) default true
25383  * @cfg {Boolean} loadingText default 'Loading...'
25384  * 
25385  * @constructor
25386  * Create a new UploadCropbox
25387  * @param {Object} config The config object
25388  */
25389
25390 Roo.bootstrap.UploadCropbox = function(config){
25391     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25392     
25393     this.addEvents({
25394         /**
25395          * @event beforeselectfile
25396          * Fire before select file
25397          * @param {Roo.bootstrap.UploadCropbox} this
25398          */
25399         "beforeselectfile" : true,
25400         /**
25401          * @event initial
25402          * Fire after initEvent
25403          * @param {Roo.bootstrap.UploadCropbox} this
25404          */
25405         "initial" : true,
25406         /**
25407          * @event crop
25408          * Fire after initEvent
25409          * @param {Roo.bootstrap.UploadCropbox} this
25410          * @param {String} data
25411          */
25412         "crop" : true,
25413         /**
25414          * @event prepare
25415          * Fire when preparing the file data
25416          * @param {Roo.bootstrap.UploadCropbox} this
25417          * @param {Object} file
25418          */
25419         "prepare" : true,
25420         /**
25421          * @event exception
25422          * Fire when get exception
25423          * @param {Roo.bootstrap.UploadCropbox} this
25424          * @param {XMLHttpRequest} xhr
25425          */
25426         "exception" : true,
25427         /**
25428          * @event beforeloadcanvas
25429          * Fire before load the canvas
25430          * @param {Roo.bootstrap.UploadCropbox} this
25431          * @param {String} src
25432          */
25433         "beforeloadcanvas" : true,
25434         /**
25435          * @event trash
25436          * Fire when trash image
25437          * @param {Roo.bootstrap.UploadCropbox} this
25438          */
25439         "trash" : true,
25440         /**
25441          * @event download
25442          * Fire when download the image
25443          * @param {Roo.bootstrap.UploadCropbox} this
25444          */
25445         "download" : true,
25446         /**
25447          * @event footerbuttonclick
25448          * Fire when footerbuttonclick
25449          * @param {Roo.bootstrap.UploadCropbox} this
25450          * @param {String} type
25451          */
25452         "footerbuttonclick" : true,
25453         /**
25454          * @event resize
25455          * Fire when resize
25456          * @param {Roo.bootstrap.UploadCropbox} this
25457          */
25458         "resize" : true,
25459         /**
25460          * @event rotate
25461          * Fire when rotate the image
25462          * @param {Roo.bootstrap.UploadCropbox} this
25463          * @param {String} pos
25464          */
25465         "rotate" : true,
25466         /**
25467          * @event inspect
25468          * Fire when inspect the file
25469          * @param {Roo.bootstrap.UploadCropbox} this
25470          * @param {Object} file
25471          */
25472         "inspect" : true,
25473         /**
25474          * @event upload
25475          * Fire when xhr upload the file
25476          * @param {Roo.bootstrap.UploadCropbox} this
25477          * @param {Object} data
25478          */
25479         "upload" : true,
25480         /**
25481          * @event arrange
25482          * Fire when arrange the file data
25483          * @param {Roo.bootstrap.UploadCropbox} this
25484          * @param {Object} formData
25485          */
25486         "arrange" : true
25487     });
25488     
25489     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25490 };
25491
25492 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25493     
25494     emptyText : 'Click to upload image',
25495     rotateNotify : 'Image is too small to rotate',
25496     errorTimeout : 3000,
25497     scale : 0,
25498     baseScale : 1,
25499     rotate : 0,
25500     dragable : false,
25501     pinching : false,
25502     mouseX : 0,
25503     mouseY : 0,
25504     cropData : false,
25505     minWidth : 300,
25506     minHeight : 300,
25507     file : false,
25508     exif : {},
25509     baseRotate : 1,
25510     cropType : 'image/jpeg',
25511     buttons : false,
25512     canvasLoaded : false,
25513     isDocument : false,
25514     method : 'POST',
25515     paramName : 'imageUpload',
25516     loadMask : true,
25517     loadingText : 'Loading...',
25518     maskEl : false,
25519     
25520     getAutoCreate : function()
25521     {
25522         var cfg = {
25523             tag : 'div',
25524             cls : 'roo-upload-cropbox',
25525             cn : [
25526                 {
25527                     tag : 'input',
25528                     cls : 'roo-upload-cropbox-selector',
25529                     type : 'file'
25530                 },
25531                 {
25532                     tag : 'div',
25533                     cls : 'roo-upload-cropbox-body',
25534                     style : 'cursor:pointer',
25535                     cn : [
25536                         {
25537                             tag : 'div',
25538                             cls : 'roo-upload-cropbox-preview'
25539                         },
25540                         {
25541                             tag : 'div',
25542                             cls : 'roo-upload-cropbox-thumb'
25543                         },
25544                         {
25545                             tag : 'div',
25546                             cls : 'roo-upload-cropbox-empty-notify',
25547                             html : this.emptyText
25548                         },
25549                         {
25550                             tag : 'div',
25551                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25552                             html : this.rotateNotify
25553                         }
25554                     ]
25555                 },
25556                 {
25557                     tag : 'div',
25558                     cls : 'roo-upload-cropbox-footer',
25559                     cn : {
25560                         tag : 'div',
25561                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25562                         cn : []
25563                     }
25564                 }
25565             ]
25566         };
25567         
25568         return cfg;
25569     },
25570     
25571     onRender : function(ct, position)
25572     {
25573         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25574         
25575         if (this.buttons.length) {
25576             
25577             Roo.each(this.buttons, function(bb) {
25578                 
25579                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25580                 
25581                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25582                 
25583             }, this);
25584         }
25585         
25586         if(this.loadMask){
25587             this.maskEl = this.el;
25588         }
25589     },
25590     
25591     initEvents : function()
25592     {
25593         this.urlAPI = (window.createObjectURL && window) || 
25594                                 (window.URL && URL.revokeObjectURL && URL) || 
25595                                 (window.webkitURL && webkitURL);
25596                         
25597         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25598         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25599         
25600         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25601         this.selectorEl.hide();
25602         
25603         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25604         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25605         
25606         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25607         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25608         this.thumbEl.hide();
25609         
25610         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25611         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25612         
25613         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25614         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25615         this.errorEl.hide();
25616         
25617         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25618         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25619         this.footerEl.hide();
25620         
25621         this.setThumbBoxSize();
25622         
25623         this.bind();
25624         
25625         this.resize();
25626         
25627         this.fireEvent('initial', this);
25628     },
25629
25630     bind : function()
25631     {
25632         var _this = this;
25633         
25634         window.addEventListener("resize", function() { _this.resize(); } );
25635         
25636         this.bodyEl.on('click', this.beforeSelectFile, this);
25637         
25638         if(Roo.isTouch){
25639             this.bodyEl.on('touchstart', this.onTouchStart, this);
25640             this.bodyEl.on('touchmove', this.onTouchMove, this);
25641             this.bodyEl.on('touchend', this.onTouchEnd, this);
25642         }
25643         
25644         if(!Roo.isTouch){
25645             this.bodyEl.on('mousedown', this.onMouseDown, this);
25646             this.bodyEl.on('mousemove', this.onMouseMove, this);
25647             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25648             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25649             Roo.get(document).on('mouseup', this.onMouseUp, this);
25650         }
25651         
25652         this.selectorEl.on('change', this.onFileSelected, this);
25653     },
25654     
25655     reset : function()
25656     {    
25657         this.scale = 0;
25658         this.baseScale = 1;
25659         this.rotate = 0;
25660         this.baseRotate = 1;
25661         this.dragable = false;
25662         this.pinching = false;
25663         this.mouseX = 0;
25664         this.mouseY = 0;
25665         this.cropData = false;
25666         this.notifyEl.dom.innerHTML = this.emptyText;
25667         
25668         this.selectorEl.dom.value = '';
25669         
25670     },
25671     
25672     resize : function()
25673     {
25674         if(this.fireEvent('resize', this) != false){
25675             this.setThumbBoxPosition();
25676             this.setCanvasPosition();
25677         }
25678     },
25679     
25680     onFooterButtonClick : function(e, el, o, type)
25681     {
25682         switch (type) {
25683             case 'rotate-left' :
25684                 this.onRotateLeft(e);
25685                 break;
25686             case 'rotate-right' :
25687                 this.onRotateRight(e);
25688                 break;
25689             case 'picture' :
25690                 this.beforeSelectFile(e);
25691                 break;
25692             case 'trash' :
25693                 this.trash(e);
25694                 break;
25695             case 'crop' :
25696                 this.crop(e);
25697                 break;
25698             case 'download' :
25699                 this.download(e);
25700                 break;
25701             default :
25702                 break;
25703         }
25704         
25705         this.fireEvent('footerbuttonclick', this, type);
25706     },
25707     
25708     beforeSelectFile : function(e)
25709     {
25710         e.preventDefault();
25711         
25712         if(this.fireEvent('beforeselectfile', this) != false){
25713             this.selectorEl.dom.click();
25714         }
25715     },
25716     
25717     onFileSelected : function(e)
25718     {
25719         e.preventDefault();
25720         
25721         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25722             return;
25723         }
25724         
25725         var file = this.selectorEl.dom.files[0];
25726         
25727         if(this.fireEvent('inspect', this, file) != false){
25728             this.prepare(file);
25729         }
25730         
25731     },
25732     
25733     trash : function(e)
25734     {
25735         this.fireEvent('trash', this);
25736     },
25737     
25738     download : function(e)
25739     {
25740         this.fireEvent('download', this);
25741     },
25742     
25743     loadCanvas : function(src)
25744     {   
25745         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25746             
25747             this.reset();
25748             
25749             this.imageEl = document.createElement('img');
25750             
25751             var _this = this;
25752             
25753             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25754             
25755             this.imageEl.src = src;
25756         }
25757     },
25758     
25759     onLoadCanvas : function()
25760     {   
25761         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25762         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25763         
25764         this.bodyEl.un('click', this.beforeSelectFile, this);
25765         
25766         this.notifyEl.hide();
25767         this.thumbEl.show();
25768         this.footerEl.show();
25769         
25770         this.baseRotateLevel();
25771         
25772         if(this.isDocument){
25773             this.setThumbBoxSize();
25774         }
25775         
25776         this.setThumbBoxPosition();
25777         
25778         this.baseScaleLevel();
25779         
25780         this.draw();
25781         
25782         this.resize();
25783         
25784         this.canvasLoaded = true;
25785         
25786         if(this.loadMask){
25787             this.maskEl.unmask();
25788         }
25789         
25790     },
25791     
25792     setCanvasPosition : function()
25793     {   
25794         if(!this.canvasEl){
25795             return;
25796         }
25797         
25798         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25799         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25800         
25801         this.previewEl.setLeft(pw);
25802         this.previewEl.setTop(ph);
25803         
25804     },
25805     
25806     onMouseDown : function(e)
25807     {   
25808         e.stopEvent();
25809         
25810         this.dragable = true;
25811         this.pinching = false;
25812         
25813         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25814             this.dragable = false;
25815             return;
25816         }
25817         
25818         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25819         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25820         
25821     },
25822     
25823     onMouseMove : function(e)
25824     {   
25825         e.stopEvent();
25826         
25827         if(!this.canvasLoaded){
25828             return;
25829         }
25830         
25831         if (!this.dragable){
25832             return;
25833         }
25834         
25835         var minX = Math.ceil(this.thumbEl.getLeft(true));
25836         var minY = Math.ceil(this.thumbEl.getTop(true));
25837         
25838         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25839         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25840         
25841         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25842         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25843         
25844         x = x - this.mouseX;
25845         y = y - this.mouseY;
25846         
25847         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25848         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25849         
25850         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25851         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25852         
25853         this.previewEl.setLeft(bgX);
25854         this.previewEl.setTop(bgY);
25855         
25856         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25857         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25858     },
25859     
25860     onMouseUp : function(e)
25861     {   
25862         e.stopEvent();
25863         
25864         this.dragable = false;
25865     },
25866     
25867     onMouseWheel : function(e)
25868     {   
25869         e.stopEvent();
25870         
25871         this.startScale = this.scale;
25872         
25873         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25874         
25875         if(!this.zoomable()){
25876             this.scale = this.startScale;
25877             return;
25878         }
25879         
25880         this.draw();
25881         
25882         return;
25883     },
25884     
25885     zoomable : function()
25886     {
25887         var minScale = this.thumbEl.getWidth() / this.minWidth;
25888         
25889         if(this.minWidth < this.minHeight){
25890             minScale = this.thumbEl.getHeight() / this.minHeight;
25891         }
25892         
25893         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25894         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25895         
25896         if(
25897                 this.isDocument &&
25898                 (this.rotate == 0 || this.rotate == 180) && 
25899                 (
25900                     width > this.imageEl.OriginWidth || 
25901                     height > this.imageEl.OriginHeight ||
25902                     (width < this.minWidth && height < this.minHeight)
25903                 )
25904         ){
25905             return false;
25906         }
25907         
25908         if(
25909                 this.isDocument &&
25910                 (this.rotate == 90 || this.rotate == 270) && 
25911                 (
25912                     width > this.imageEl.OriginWidth || 
25913                     height > this.imageEl.OriginHeight ||
25914                     (width < this.minHeight && height < this.minWidth)
25915                 )
25916         ){
25917             return false;
25918         }
25919         
25920         if(
25921                 !this.isDocument &&
25922                 (this.rotate == 0 || this.rotate == 180) && 
25923                 (
25924                     width < this.minWidth || 
25925                     width > this.imageEl.OriginWidth || 
25926                     height < this.minHeight || 
25927                     height > this.imageEl.OriginHeight
25928                 )
25929         ){
25930             return false;
25931         }
25932         
25933         if(
25934                 !this.isDocument &&
25935                 (this.rotate == 90 || this.rotate == 270) && 
25936                 (
25937                     width < this.minHeight || 
25938                     width > this.imageEl.OriginWidth || 
25939                     height < this.minWidth || 
25940                     height > this.imageEl.OriginHeight
25941                 )
25942         ){
25943             return false;
25944         }
25945         
25946         return true;
25947         
25948     },
25949     
25950     onRotateLeft : function(e)
25951     {   
25952         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25953             
25954             var minScale = this.thumbEl.getWidth() / this.minWidth;
25955             
25956             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25957             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25958             
25959             this.startScale = this.scale;
25960             
25961             while (this.getScaleLevel() < minScale){
25962             
25963                 this.scale = this.scale + 1;
25964                 
25965                 if(!this.zoomable()){
25966                     break;
25967                 }
25968                 
25969                 if(
25970                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25971                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25972                 ){
25973                     continue;
25974                 }
25975                 
25976                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25977
25978                 this.draw();
25979                 
25980                 return;
25981             }
25982             
25983             this.scale = this.startScale;
25984             
25985             this.onRotateFail();
25986             
25987             return false;
25988         }
25989         
25990         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25991
25992         if(this.isDocument){
25993             this.setThumbBoxSize();
25994             this.setThumbBoxPosition();
25995             this.setCanvasPosition();
25996         }
25997         
25998         this.draw();
25999         
26000         this.fireEvent('rotate', this, 'left');
26001         
26002     },
26003     
26004     onRotateRight : function(e)
26005     {
26006         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26007             
26008             var minScale = this.thumbEl.getWidth() / this.minWidth;
26009         
26010             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26011             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26012             
26013             this.startScale = this.scale;
26014             
26015             while (this.getScaleLevel() < minScale){
26016             
26017                 this.scale = this.scale + 1;
26018                 
26019                 if(!this.zoomable()){
26020                     break;
26021                 }
26022                 
26023                 if(
26024                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26025                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26026                 ){
26027                     continue;
26028                 }
26029                 
26030                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26031
26032                 this.draw();
26033                 
26034                 return;
26035             }
26036             
26037             this.scale = this.startScale;
26038             
26039             this.onRotateFail();
26040             
26041             return false;
26042         }
26043         
26044         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26045
26046         if(this.isDocument){
26047             this.setThumbBoxSize();
26048             this.setThumbBoxPosition();
26049             this.setCanvasPosition();
26050         }
26051         
26052         this.draw();
26053         
26054         this.fireEvent('rotate', this, 'right');
26055     },
26056     
26057     onRotateFail : function()
26058     {
26059         this.errorEl.show(true);
26060         
26061         var _this = this;
26062         
26063         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26064     },
26065     
26066     draw : function()
26067     {
26068         this.previewEl.dom.innerHTML = '';
26069         
26070         var canvasEl = document.createElement("canvas");
26071         
26072         var contextEl = canvasEl.getContext("2d");
26073         
26074         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26075         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26076         var center = this.imageEl.OriginWidth / 2;
26077         
26078         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26079             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26080             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26081             center = this.imageEl.OriginHeight / 2;
26082         }
26083         
26084         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26085         
26086         contextEl.translate(center, center);
26087         contextEl.rotate(this.rotate * Math.PI / 180);
26088
26089         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26090         
26091         this.canvasEl = document.createElement("canvas");
26092         
26093         this.contextEl = this.canvasEl.getContext("2d");
26094         
26095         switch (this.rotate) {
26096             case 0 :
26097                 
26098                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26099                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26100                 
26101                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26102                 
26103                 break;
26104             case 90 : 
26105                 
26106                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26107                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26108                 
26109                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26110                     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);
26111                     break;
26112                 }
26113                 
26114                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26115                 
26116                 break;
26117             case 180 :
26118                 
26119                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26120                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26121                 
26122                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26123                     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);
26124                     break;
26125                 }
26126                 
26127                 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);
26128                 
26129                 break;
26130             case 270 :
26131                 
26132                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26133                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26134         
26135                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26136                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26137                     break;
26138                 }
26139                 
26140                 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);
26141                 
26142                 break;
26143             default : 
26144                 break;
26145         }
26146         
26147         this.previewEl.appendChild(this.canvasEl);
26148         
26149         this.setCanvasPosition();
26150     },
26151     
26152     crop : function()
26153     {
26154         if(!this.canvasLoaded){
26155             return;
26156         }
26157         
26158         var imageCanvas = document.createElement("canvas");
26159         
26160         var imageContext = imageCanvas.getContext("2d");
26161         
26162         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26163         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26164         
26165         var center = imageCanvas.width / 2;
26166         
26167         imageContext.translate(center, center);
26168         
26169         imageContext.rotate(this.rotate * Math.PI / 180);
26170         
26171         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26172         
26173         var canvas = document.createElement("canvas");
26174         
26175         var context = canvas.getContext("2d");
26176                 
26177         canvas.width = this.minWidth;
26178         canvas.height = this.minHeight;
26179
26180         switch (this.rotate) {
26181             case 0 :
26182                 
26183                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26184                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26185                 
26186                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26187                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26188                 
26189                 var targetWidth = this.minWidth - 2 * x;
26190                 var targetHeight = this.minHeight - 2 * y;
26191                 
26192                 var scale = 1;
26193                 
26194                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26195                     scale = targetWidth / width;
26196                 }
26197                 
26198                 if(x > 0 && y == 0){
26199                     scale = targetHeight / height;
26200                 }
26201                 
26202                 if(x > 0 && y > 0){
26203                     scale = targetWidth / width;
26204                     
26205                     if(width < height){
26206                         scale = targetHeight / height;
26207                     }
26208                 }
26209                 
26210                 context.scale(scale, scale);
26211                 
26212                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26213                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26214
26215                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26216                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26217
26218                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26219                 
26220                 break;
26221             case 90 : 
26222                 
26223                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26224                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26225                 
26226                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26227                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26228                 
26229                 var targetWidth = this.minWidth - 2 * x;
26230                 var targetHeight = this.minHeight - 2 * y;
26231                 
26232                 var scale = 1;
26233                 
26234                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26235                     scale = targetWidth / width;
26236                 }
26237                 
26238                 if(x > 0 && y == 0){
26239                     scale = targetHeight / height;
26240                 }
26241                 
26242                 if(x > 0 && y > 0){
26243                     scale = targetWidth / width;
26244                     
26245                     if(width < height){
26246                         scale = targetHeight / height;
26247                     }
26248                 }
26249                 
26250                 context.scale(scale, scale);
26251                 
26252                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26253                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26254
26255                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26256                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26257                 
26258                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26259                 
26260                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26261                 
26262                 break;
26263             case 180 :
26264                 
26265                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26266                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26267                 
26268                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26269                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26270                 
26271                 var targetWidth = this.minWidth - 2 * x;
26272                 var targetHeight = this.minHeight - 2 * y;
26273                 
26274                 var scale = 1;
26275                 
26276                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26277                     scale = targetWidth / width;
26278                 }
26279                 
26280                 if(x > 0 && y == 0){
26281                     scale = targetHeight / height;
26282                 }
26283                 
26284                 if(x > 0 && y > 0){
26285                     scale = targetWidth / width;
26286                     
26287                     if(width < height){
26288                         scale = targetHeight / height;
26289                     }
26290                 }
26291                 
26292                 context.scale(scale, scale);
26293                 
26294                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26295                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26296
26297                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26298                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26299
26300                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26301                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26302                 
26303                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26304                 
26305                 break;
26306             case 270 :
26307                 
26308                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26309                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26310                 
26311                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26312                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26313                 
26314                 var targetWidth = this.minWidth - 2 * x;
26315                 var targetHeight = this.minHeight - 2 * y;
26316                 
26317                 var scale = 1;
26318                 
26319                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26320                     scale = targetWidth / width;
26321                 }
26322                 
26323                 if(x > 0 && y == 0){
26324                     scale = targetHeight / height;
26325                 }
26326                 
26327                 if(x > 0 && y > 0){
26328                     scale = targetWidth / width;
26329                     
26330                     if(width < height){
26331                         scale = targetHeight / height;
26332                     }
26333                 }
26334                 
26335                 context.scale(scale, scale);
26336                 
26337                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26338                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26339
26340                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26341                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26342                 
26343                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26344                 
26345                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26346                 
26347                 break;
26348             default : 
26349                 break;
26350         }
26351         
26352         this.cropData = canvas.toDataURL(this.cropType);
26353         
26354         if(this.fireEvent('crop', this, this.cropData) !== false){
26355             this.process(this.file, this.cropData);
26356         }
26357         
26358         return;
26359         
26360     },
26361     
26362     setThumbBoxSize : function()
26363     {
26364         var width, height;
26365         
26366         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26367             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26368             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26369             
26370             this.minWidth = width;
26371             this.minHeight = height;
26372             
26373             if(this.rotate == 90 || this.rotate == 270){
26374                 this.minWidth = height;
26375                 this.minHeight = width;
26376             }
26377         }
26378         
26379         height = 300;
26380         width = Math.ceil(this.minWidth * height / this.minHeight);
26381         
26382         if(this.minWidth > this.minHeight){
26383             width = 300;
26384             height = Math.ceil(this.minHeight * width / this.minWidth);
26385         }
26386         
26387         this.thumbEl.setStyle({
26388             width : width + 'px',
26389             height : height + 'px'
26390         });
26391
26392         return;
26393             
26394     },
26395     
26396     setThumbBoxPosition : function()
26397     {
26398         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26399         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26400         
26401         this.thumbEl.setLeft(x);
26402         this.thumbEl.setTop(y);
26403         
26404     },
26405     
26406     baseRotateLevel : function()
26407     {
26408         this.baseRotate = 1;
26409         
26410         if(
26411                 typeof(this.exif) != 'undefined' &&
26412                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26413                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26414         ){
26415             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26416         }
26417         
26418         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26419         
26420     },
26421     
26422     baseScaleLevel : function()
26423     {
26424         var width, height;
26425         
26426         if(this.isDocument){
26427             
26428             if(this.baseRotate == 6 || this.baseRotate == 8){
26429             
26430                 height = this.thumbEl.getHeight();
26431                 this.baseScale = height / this.imageEl.OriginWidth;
26432
26433                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26434                     width = this.thumbEl.getWidth();
26435                     this.baseScale = width / this.imageEl.OriginHeight;
26436                 }
26437
26438                 return;
26439             }
26440
26441             height = this.thumbEl.getHeight();
26442             this.baseScale = height / this.imageEl.OriginHeight;
26443
26444             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26445                 width = this.thumbEl.getWidth();
26446                 this.baseScale = width / this.imageEl.OriginWidth;
26447             }
26448
26449             return;
26450         }
26451         
26452         if(this.baseRotate == 6 || this.baseRotate == 8){
26453             
26454             width = this.thumbEl.getHeight();
26455             this.baseScale = width / this.imageEl.OriginHeight;
26456             
26457             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26458                 height = this.thumbEl.getWidth();
26459                 this.baseScale = height / this.imageEl.OriginHeight;
26460             }
26461             
26462             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26463                 height = this.thumbEl.getWidth();
26464                 this.baseScale = height / this.imageEl.OriginHeight;
26465                 
26466                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26467                     width = this.thumbEl.getHeight();
26468                     this.baseScale = width / this.imageEl.OriginWidth;
26469                 }
26470             }
26471             
26472             return;
26473         }
26474         
26475         width = this.thumbEl.getWidth();
26476         this.baseScale = width / this.imageEl.OriginWidth;
26477         
26478         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26479             height = this.thumbEl.getHeight();
26480             this.baseScale = height / this.imageEl.OriginHeight;
26481         }
26482         
26483         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26484             
26485             height = this.thumbEl.getHeight();
26486             this.baseScale = height / this.imageEl.OriginHeight;
26487             
26488             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26489                 width = this.thumbEl.getWidth();
26490                 this.baseScale = width / this.imageEl.OriginWidth;
26491             }
26492             
26493         }
26494         
26495         return;
26496     },
26497     
26498     getScaleLevel : function()
26499     {
26500         return this.baseScale * Math.pow(1.1, this.scale);
26501     },
26502     
26503     onTouchStart : function(e)
26504     {
26505         if(!this.canvasLoaded){
26506             this.beforeSelectFile(e);
26507             return;
26508         }
26509         
26510         var touches = e.browserEvent.touches;
26511         
26512         if(!touches){
26513             return;
26514         }
26515         
26516         if(touches.length == 1){
26517             this.onMouseDown(e);
26518             return;
26519         }
26520         
26521         if(touches.length != 2){
26522             return;
26523         }
26524         
26525         var coords = [];
26526         
26527         for(var i = 0, finger; finger = touches[i]; i++){
26528             coords.push(finger.pageX, finger.pageY);
26529         }
26530         
26531         var x = Math.pow(coords[0] - coords[2], 2);
26532         var y = Math.pow(coords[1] - coords[3], 2);
26533         
26534         this.startDistance = Math.sqrt(x + y);
26535         
26536         this.startScale = this.scale;
26537         
26538         this.pinching = true;
26539         this.dragable = false;
26540         
26541     },
26542     
26543     onTouchMove : function(e)
26544     {
26545         if(!this.pinching && !this.dragable){
26546             return;
26547         }
26548         
26549         var touches = e.browserEvent.touches;
26550         
26551         if(!touches){
26552             return;
26553         }
26554         
26555         if(this.dragable){
26556             this.onMouseMove(e);
26557             return;
26558         }
26559         
26560         var coords = [];
26561         
26562         for(var i = 0, finger; finger = touches[i]; i++){
26563             coords.push(finger.pageX, finger.pageY);
26564         }
26565         
26566         var x = Math.pow(coords[0] - coords[2], 2);
26567         var y = Math.pow(coords[1] - coords[3], 2);
26568         
26569         this.endDistance = Math.sqrt(x + y);
26570         
26571         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26572         
26573         if(!this.zoomable()){
26574             this.scale = this.startScale;
26575             return;
26576         }
26577         
26578         this.draw();
26579         
26580     },
26581     
26582     onTouchEnd : function(e)
26583     {
26584         this.pinching = false;
26585         this.dragable = false;
26586         
26587     },
26588     
26589     process : function(file, crop)
26590     {
26591         if(this.loadMask){
26592             this.maskEl.mask(this.loadingText);
26593         }
26594         
26595         this.xhr = new XMLHttpRequest();
26596         
26597         file.xhr = this.xhr;
26598
26599         this.xhr.open(this.method, this.url, true);
26600         
26601         var headers = {
26602             "Accept": "application/json",
26603             "Cache-Control": "no-cache",
26604             "X-Requested-With": "XMLHttpRequest"
26605         };
26606         
26607         for (var headerName in headers) {
26608             var headerValue = headers[headerName];
26609             if (headerValue) {
26610                 this.xhr.setRequestHeader(headerName, headerValue);
26611             }
26612         }
26613         
26614         var _this = this;
26615         
26616         this.xhr.onload = function()
26617         {
26618             _this.xhrOnLoad(_this.xhr);
26619         }
26620         
26621         this.xhr.onerror = function()
26622         {
26623             _this.xhrOnError(_this.xhr);
26624         }
26625         
26626         var formData = new FormData();
26627
26628         formData.append('returnHTML', 'NO');
26629         
26630         if(crop){
26631             formData.append('crop', crop);
26632         }
26633         
26634         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26635             formData.append(this.paramName, file, file.name);
26636         }
26637         
26638         if(typeof(file.filename) != 'undefined'){
26639             formData.append('filename', file.filename);
26640         }
26641         
26642         if(typeof(file.mimetype) != 'undefined'){
26643             formData.append('mimetype', file.mimetype);
26644         }
26645         
26646         if(this.fireEvent('arrange', this, formData) != false){
26647             this.xhr.send(formData);
26648         };
26649     },
26650     
26651     xhrOnLoad : function(xhr)
26652     {
26653         if(this.loadMask){
26654             this.maskEl.unmask();
26655         }
26656         
26657         if (xhr.readyState !== 4) {
26658             this.fireEvent('exception', this, xhr);
26659             return;
26660         }
26661
26662         var response = Roo.decode(xhr.responseText);
26663         
26664         if(!response.success){
26665             this.fireEvent('exception', this, xhr);
26666             return;
26667         }
26668         
26669         var response = Roo.decode(xhr.responseText);
26670         
26671         this.fireEvent('upload', this, response);
26672         
26673     },
26674     
26675     xhrOnError : function()
26676     {
26677         if(this.loadMask){
26678             this.maskEl.unmask();
26679         }
26680         
26681         Roo.log('xhr on error');
26682         
26683         var response = Roo.decode(xhr.responseText);
26684           
26685         Roo.log(response);
26686         
26687     },
26688     
26689     prepare : function(file)
26690     {   
26691         if(this.loadMask){
26692             this.maskEl.mask(this.loadingText);
26693         }
26694         
26695         this.file = false;
26696         this.exif = {};
26697         
26698         if(typeof(file) === 'string'){
26699             this.loadCanvas(file);
26700             return;
26701         }
26702         
26703         if(!file || !this.urlAPI){
26704             return;
26705         }
26706         
26707         this.file = file;
26708         this.cropType = file.type;
26709         
26710         var _this = this;
26711         
26712         if(this.fireEvent('prepare', this, this.file) != false){
26713             
26714             var reader = new FileReader();
26715             
26716             reader.onload = function (e) {
26717                 if (e.target.error) {
26718                     Roo.log(e.target.error);
26719                     return;
26720                 }
26721                 
26722                 var buffer = e.target.result,
26723                     dataView = new DataView(buffer),
26724                     offset = 2,
26725                     maxOffset = dataView.byteLength - 4,
26726                     markerBytes,
26727                     markerLength;
26728                 
26729                 if (dataView.getUint16(0) === 0xffd8) {
26730                     while (offset < maxOffset) {
26731                         markerBytes = dataView.getUint16(offset);
26732                         
26733                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26734                             markerLength = dataView.getUint16(offset + 2) + 2;
26735                             if (offset + markerLength > dataView.byteLength) {
26736                                 Roo.log('Invalid meta data: Invalid segment size.');
26737                                 break;
26738                             }
26739                             
26740                             if(markerBytes == 0xffe1){
26741                                 _this.parseExifData(
26742                                     dataView,
26743                                     offset,
26744                                     markerLength
26745                                 );
26746                             }
26747                             
26748                             offset += markerLength;
26749                             
26750                             continue;
26751                         }
26752                         
26753                         break;
26754                     }
26755                     
26756                 }
26757                 
26758                 var url = _this.urlAPI.createObjectURL(_this.file);
26759                 
26760                 _this.loadCanvas(url);
26761                 
26762                 return;
26763             }
26764             
26765             reader.readAsArrayBuffer(this.file);
26766             
26767         }
26768         
26769     },
26770     
26771     parseExifData : function(dataView, offset, length)
26772     {
26773         var tiffOffset = offset + 10,
26774             littleEndian,
26775             dirOffset;
26776     
26777         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26778             // No Exif data, might be XMP data instead
26779             return;
26780         }
26781         
26782         // Check for the ASCII code for "Exif" (0x45786966):
26783         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26784             // No Exif data, might be XMP data instead
26785             return;
26786         }
26787         if (tiffOffset + 8 > dataView.byteLength) {
26788             Roo.log('Invalid Exif data: Invalid segment size.');
26789             return;
26790         }
26791         // Check for the two null bytes:
26792         if (dataView.getUint16(offset + 8) !== 0x0000) {
26793             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26794             return;
26795         }
26796         // Check the byte alignment:
26797         switch (dataView.getUint16(tiffOffset)) {
26798         case 0x4949:
26799             littleEndian = true;
26800             break;
26801         case 0x4D4D:
26802             littleEndian = false;
26803             break;
26804         default:
26805             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26806             return;
26807         }
26808         // Check for the TIFF tag marker (0x002A):
26809         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26810             Roo.log('Invalid Exif data: Missing TIFF marker.');
26811             return;
26812         }
26813         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26814         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26815         
26816         this.parseExifTags(
26817             dataView,
26818             tiffOffset,
26819             tiffOffset + dirOffset,
26820             littleEndian
26821         );
26822     },
26823     
26824     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26825     {
26826         var tagsNumber,
26827             dirEndOffset,
26828             i;
26829         if (dirOffset + 6 > dataView.byteLength) {
26830             Roo.log('Invalid Exif data: Invalid directory offset.');
26831             return;
26832         }
26833         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26834         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26835         if (dirEndOffset + 4 > dataView.byteLength) {
26836             Roo.log('Invalid Exif data: Invalid directory size.');
26837             return;
26838         }
26839         for (i = 0; i < tagsNumber; i += 1) {
26840             this.parseExifTag(
26841                 dataView,
26842                 tiffOffset,
26843                 dirOffset + 2 + 12 * i, // tag offset
26844                 littleEndian
26845             );
26846         }
26847         // Return the offset to the next directory:
26848         return dataView.getUint32(dirEndOffset, littleEndian);
26849     },
26850     
26851     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26852     {
26853         var tag = dataView.getUint16(offset, littleEndian);
26854         
26855         this.exif[tag] = this.getExifValue(
26856             dataView,
26857             tiffOffset,
26858             offset,
26859             dataView.getUint16(offset + 2, littleEndian), // tag type
26860             dataView.getUint32(offset + 4, littleEndian), // tag length
26861             littleEndian
26862         );
26863     },
26864     
26865     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26866     {
26867         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26868             tagSize,
26869             dataOffset,
26870             values,
26871             i,
26872             str,
26873             c;
26874     
26875         if (!tagType) {
26876             Roo.log('Invalid Exif data: Invalid tag type.');
26877             return;
26878         }
26879         
26880         tagSize = tagType.size * length;
26881         // Determine if the value is contained in the dataOffset bytes,
26882         // or if the value at the dataOffset is a pointer to the actual data:
26883         dataOffset = tagSize > 4 ?
26884                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26885         if (dataOffset + tagSize > dataView.byteLength) {
26886             Roo.log('Invalid Exif data: Invalid data offset.');
26887             return;
26888         }
26889         if (length === 1) {
26890             return tagType.getValue(dataView, dataOffset, littleEndian);
26891         }
26892         values = [];
26893         for (i = 0; i < length; i += 1) {
26894             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26895         }
26896         
26897         if (tagType.ascii) {
26898             str = '';
26899             // Concatenate the chars:
26900             for (i = 0; i < values.length; i += 1) {
26901                 c = values[i];
26902                 // Ignore the terminating NULL byte(s):
26903                 if (c === '\u0000') {
26904                     break;
26905                 }
26906                 str += c;
26907             }
26908             return str;
26909         }
26910         return values;
26911     }
26912     
26913 });
26914
26915 Roo.apply(Roo.bootstrap.UploadCropbox, {
26916     tags : {
26917         'Orientation': 0x0112
26918     },
26919     
26920     Orientation: {
26921             1: 0, //'top-left',
26922 //            2: 'top-right',
26923             3: 180, //'bottom-right',
26924 //            4: 'bottom-left',
26925 //            5: 'left-top',
26926             6: 90, //'right-top',
26927 //            7: 'right-bottom',
26928             8: 270 //'left-bottom'
26929     },
26930     
26931     exifTagTypes : {
26932         // byte, 8-bit unsigned int:
26933         1: {
26934             getValue: function (dataView, dataOffset) {
26935                 return dataView.getUint8(dataOffset);
26936             },
26937             size: 1
26938         },
26939         // ascii, 8-bit byte:
26940         2: {
26941             getValue: function (dataView, dataOffset) {
26942                 return String.fromCharCode(dataView.getUint8(dataOffset));
26943             },
26944             size: 1,
26945             ascii: true
26946         },
26947         // short, 16 bit int:
26948         3: {
26949             getValue: function (dataView, dataOffset, littleEndian) {
26950                 return dataView.getUint16(dataOffset, littleEndian);
26951             },
26952             size: 2
26953         },
26954         // long, 32 bit int:
26955         4: {
26956             getValue: function (dataView, dataOffset, littleEndian) {
26957                 return dataView.getUint32(dataOffset, littleEndian);
26958             },
26959             size: 4
26960         },
26961         // rational = two long values, first is numerator, second is denominator:
26962         5: {
26963             getValue: function (dataView, dataOffset, littleEndian) {
26964                 return dataView.getUint32(dataOffset, littleEndian) /
26965                     dataView.getUint32(dataOffset + 4, littleEndian);
26966             },
26967             size: 8
26968         },
26969         // slong, 32 bit signed int:
26970         9: {
26971             getValue: function (dataView, dataOffset, littleEndian) {
26972                 return dataView.getInt32(dataOffset, littleEndian);
26973             },
26974             size: 4
26975         },
26976         // srational, two slongs, first is numerator, second is denominator:
26977         10: {
26978             getValue: function (dataView, dataOffset, littleEndian) {
26979                 return dataView.getInt32(dataOffset, littleEndian) /
26980                     dataView.getInt32(dataOffset + 4, littleEndian);
26981             },
26982             size: 8
26983         }
26984     },
26985     
26986     footer : {
26987         STANDARD : [
26988             {
26989                 tag : 'div',
26990                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26991                 action : 'rotate-left',
26992                 cn : [
26993                     {
26994                         tag : 'button',
26995                         cls : 'btn btn-default',
26996                         html : '<i class="fa fa-undo"></i>'
26997                     }
26998                 ]
26999             },
27000             {
27001                 tag : 'div',
27002                 cls : 'btn-group roo-upload-cropbox-picture',
27003                 action : 'picture',
27004                 cn : [
27005                     {
27006                         tag : 'button',
27007                         cls : 'btn btn-default',
27008                         html : '<i class="fa fa-picture-o"></i>'
27009                     }
27010                 ]
27011             },
27012             {
27013                 tag : 'div',
27014                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27015                 action : 'rotate-right',
27016                 cn : [
27017                     {
27018                         tag : 'button',
27019                         cls : 'btn btn-default',
27020                         html : '<i class="fa fa-repeat"></i>'
27021                     }
27022                 ]
27023             }
27024         ],
27025         DOCUMENT : [
27026             {
27027                 tag : 'div',
27028                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27029                 action : 'rotate-left',
27030                 cn : [
27031                     {
27032                         tag : 'button',
27033                         cls : 'btn btn-default',
27034                         html : '<i class="fa fa-undo"></i>'
27035                     }
27036                 ]
27037             },
27038             {
27039                 tag : 'div',
27040                 cls : 'btn-group roo-upload-cropbox-download',
27041                 action : 'download',
27042                 cn : [
27043                     {
27044                         tag : 'button',
27045                         cls : 'btn btn-default',
27046                         html : '<i class="fa fa-download"></i>'
27047                     }
27048                 ]
27049             },
27050             {
27051                 tag : 'div',
27052                 cls : 'btn-group roo-upload-cropbox-crop',
27053                 action : 'crop',
27054                 cn : [
27055                     {
27056                         tag : 'button',
27057                         cls : 'btn btn-default',
27058                         html : '<i class="fa fa-crop"></i>'
27059                     }
27060                 ]
27061             },
27062             {
27063                 tag : 'div',
27064                 cls : 'btn-group roo-upload-cropbox-trash',
27065                 action : 'trash',
27066                 cn : [
27067                     {
27068                         tag : 'button',
27069                         cls : 'btn btn-default',
27070                         html : '<i class="fa fa-trash"></i>'
27071                     }
27072                 ]
27073             },
27074             {
27075                 tag : 'div',
27076                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27077                 action : 'rotate-right',
27078                 cn : [
27079                     {
27080                         tag : 'button',
27081                         cls : 'btn btn-default',
27082                         html : '<i class="fa fa-repeat"></i>'
27083                     }
27084                 ]
27085             }
27086         ],
27087         ROTATOR : [
27088             {
27089                 tag : 'div',
27090                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27091                 action : 'rotate-left',
27092                 cn : [
27093                     {
27094                         tag : 'button',
27095                         cls : 'btn btn-default',
27096                         html : '<i class="fa fa-undo"></i>'
27097                     }
27098                 ]
27099             },
27100             {
27101                 tag : 'div',
27102                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27103                 action : 'rotate-right',
27104                 cn : [
27105                     {
27106                         tag : 'button',
27107                         cls : 'btn btn-default',
27108                         html : '<i class="fa fa-repeat"></i>'
27109                     }
27110                 ]
27111             }
27112         ]
27113     }
27114 });
27115
27116 /*
27117 * Licence: LGPL
27118 */
27119
27120 /**
27121  * @class Roo.bootstrap.DocumentManager
27122  * @extends Roo.bootstrap.Component
27123  * Bootstrap DocumentManager class
27124  * @cfg {String} paramName default 'imageUpload'
27125  * @cfg {String} method default POST
27126  * @cfg {String} url action url
27127  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27128  * @cfg {Boolean} multiple multiple upload default true
27129  * @cfg {Number} thumbSize default 300
27130  * @cfg {String} fieldLabel
27131  * @cfg {Number} labelWidth default 4
27132  * @cfg {String} labelAlign (left|top) default left
27133  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27134  * 
27135  * @constructor
27136  * Create a new DocumentManager
27137  * @param {Object} config The config object
27138  */
27139
27140 Roo.bootstrap.DocumentManager = function(config){
27141     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27142     
27143     this.addEvents({
27144         /**
27145          * @event initial
27146          * Fire when initial the DocumentManager
27147          * @param {Roo.bootstrap.DocumentManager} this
27148          */
27149         "initial" : true,
27150         /**
27151          * @event inspect
27152          * inspect selected file
27153          * @param {Roo.bootstrap.DocumentManager} this
27154          * @param {File} file
27155          */
27156         "inspect" : true,
27157         /**
27158          * @event exception
27159          * Fire when xhr load exception
27160          * @param {Roo.bootstrap.DocumentManager} this
27161          * @param {XMLHttpRequest} xhr
27162          */
27163         "exception" : true,
27164         /**
27165          * @event prepare
27166          * prepare the form data
27167          * @param {Roo.bootstrap.DocumentManager} this
27168          * @param {Object} formData
27169          */
27170         "prepare" : true,
27171         /**
27172          * @event remove
27173          * Fire when remove the file
27174          * @param {Roo.bootstrap.DocumentManager} this
27175          * @param {Object} file
27176          */
27177         "remove" : true,
27178         /**
27179          * @event refresh
27180          * Fire after refresh the file
27181          * @param {Roo.bootstrap.DocumentManager} this
27182          */
27183         "refresh" : true,
27184         /**
27185          * @event click
27186          * Fire after click the image
27187          * @param {Roo.bootstrap.DocumentManager} this
27188          * @param {Object} file
27189          */
27190         "click" : true,
27191         /**
27192          * @event edit
27193          * Fire when upload a image and editable set to true
27194          * @param {Roo.bootstrap.DocumentManager} this
27195          * @param {Object} file
27196          */
27197         "edit" : true,
27198         /**
27199          * @event beforeselectfile
27200          * Fire before select file
27201          * @param {Roo.bootstrap.DocumentManager} this
27202          */
27203         "beforeselectfile" : true,
27204         /**
27205          * @event process
27206          * Fire before process file
27207          * @param {Roo.bootstrap.DocumentManager} this
27208          * @param {Object} file
27209          */
27210         "process" : true
27211         
27212     });
27213 };
27214
27215 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27216     
27217     boxes : 0,
27218     inputName : '',
27219     thumbSize : 300,
27220     multiple : true,
27221     files : [],
27222     method : 'POST',
27223     url : '',
27224     paramName : 'imageUpload',
27225     fieldLabel : '',
27226     labelWidth : 4,
27227     labelAlign : 'left',
27228     editable : true,
27229     delegates : [],
27230     
27231     
27232     xhr : false, 
27233     
27234     getAutoCreate : function()
27235     {   
27236         var managerWidget = {
27237             tag : 'div',
27238             cls : 'roo-document-manager',
27239             cn : [
27240                 {
27241                     tag : 'input',
27242                     cls : 'roo-document-manager-selector',
27243                     type : 'file'
27244                 },
27245                 {
27246                     tag : 'div',
27247                     cls : 'roo-document-manager-uploader',
27248                     cn : [
27249                         {
27250                             tag : 'div',
27251                             cls : 'roo-document-manager-upload-btn',
27252                             html : '<i class="fa fa-plus"></i>'
27253                         }
27254                     ]
27255                     
27256                 }
27257             ]
27258         };
27259         
27260         var content = [
27261             {
27262                 tag : 'div',
27263                 cls : 'column col-md-12',
27264                 cn : managerWidget
27265             }
27266         ];
27267         
27268         if(this.fieldLabel.length){
27269             
27270             content = [
27271                 {
27272                     tag : 'div',
27273                     cls : 'column col-md-12',
27274                     html : this.fieldLabel
27275                 },
27276                 {
27277                     tag : 'div',
27278                     cls : 'column col-md-12',
27279                     cn : managerWidget
27280                 }
27281             ];
27282
27283             if(this.labelAlign == 'left'){
27284                 content = [
27285                     {
27286                         tag : 'div',
27287                         cls : 'column col-md-' + this.labelWidth,
27288                         html : this.fieldLabel
27289                     },
27290                     {
27291                         tag : 'div',
27292                         cls : 'column col-md-' + (12 - this.labelWidth),
27293                         cn : managerWidget
27294                     }
27295                 ];
27296                 
27297             }
27298         }
27299         
27300         var cfg = {
27301             tag : 'div',
27302             cls : 'row clearfix',
27303             cn : content
27304         };
27305         
27306         return cfg;
27307         
27308     },
27309     
27310     initEvents : function()
27311     {
27312         this.managerEl = this.el.select('.roo-document-manager', true).first();
27313         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27314         
27315         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27316         this.selectorEl.hide();
27317         
27318         if(this.multiple){
27319             this.selectorEl.attr('multiple', 'multiple');
27320         }
27321         
27322         this.selectorEl.on('change', this.onFileSelected, this);
27323         
27324         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27325         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27326         
27327         this.uploader.on('click', this.onUploaderClick, this);
27328         
27329         this.renderProgressDialog();
27330         
27331         var _this = this;
27332         
27333         window.addEventListener("resize", function() { _this.refresh(); } );
27334         
27335         this.fireEvent('initial', this);
27336     },
27337     
27338     renderProgressDialog : function()
27339     {
27340         var _this = this;
27341         
27342         this.progressDialog = new Roo.bootstrap.Modal({
27343             cls : 'roo-document-manager-progress-dialog',
27344             allow_close : false,
27345             title : '',
27346             buttons : [
27347                 {
27348                     name  :'cancel',
27349                     weight : 'danger',
27350                     html : 'Cancel'
27351                 }
27352             ], 
27353             listeners : { 
27354                 btnclick : function() {
27355                     _this.uploadCancel();
27356                     this.hide();
27357                 }
27358             }
27359         });
27360          
27361         this.progressDialog.render(Roo.get(document.body));
27362          
27363         this.progress = new Roo.bootstrap.Progress({
27364             cls : 'roo-document-manager-progress',
27365             active : true,
27366             striped : true
27367         });
27368         
27369         this.progress.render(this.progressDialog.getChildContainer());
27370         
27371         this.progressBar = new Roo.bootstrap.ProgressBar({
27372             cls : 'roo-document-manager-progress-bar',
27373             aria_valuenow : 0,
27374             aria_valuemin : 0,
27375             aria_valuemax : 12,
27376             panel : 'success'
27377         });
27378         
27379         this.progressBar.render(this.progress.getChildContainer());
27380     },
27381     
27382     onUploaderClick : function(e)
27383     {
27384         e.preventDefault();
27385      
27386         if(this.fireEvent('beforeselectfile', this) != false){
27387             this.selectorEl.dom.click();
27388         }
27389         
27390     },
27391     
27392     onFileSelected : function(e)
27393     {
27394         e.preventDefault();
27395         
27396         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27397             return;
27398         }
27399         
27400         Roo.each(this.selectorEl.dom.files, function(file){
27401             if(this.fireEvent('inspect', this, file) != false){
27402                 this.files.push(file);
27403             }
27404         }, this);
27405         
27406         this.queue();
27407         
27408     },
27409     
27410     queue : function()
27411     {
27412         this.selectorEl.dom.value = '';
27413         
27414         if(!this.files.length){
27415             return;
27416         }
27417         
27418         if(this.boxes > 0 && this.files.length > this.boxes){
27419             this.files = this.files.slice(0, this.boxes);
27420         }
27421         
27422         this.uploader.show();
27423         
27424         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27425             this.uploader.hide();
27426         }
27427         
27428         var _this = this;
27429         
27430         var files = [];
27431         
27432         var docs = [];
27433         
27434         Roo.each(this.files, function(file){
27435             
27436             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27437                 var f = this.renderPreview(file);
27438                 files.push(f);
27439                 return;
27440             }
27441             
27442             if(file.type.indexOf('image') != -1){
27443                 this.delegates.push(
27444                     (function(){
27445                         _this.process(file);
27446                     }).createDelegate(this)
27447                 );
27448         
27449                 return;
27450             }
27451             
27452             docs.push(
27453                 (function(){
27454                     _this.process(file);
27455                 }).createDelegate(this)
27456             );
27457             
27458         }, this);
27459         
27460         this.files = files;
27461         
27462         this.delegates = this.delegates.concat(docs);
27463         
27464         if(!this.delegates.length){
27465             this.refresh();
27466             return;
27467         }
27468         
27469         this.progressBar.aria_valuemax = this.delegates.length;
27470         
27471         this.arrange();
27472         
27473         return;
27474     },
27475     
27476     arrange : function()
27477     {
27478         if(!this.delegates.length){
27479             this.progressDialog.hide();
27480             this.refresh();
27481             return;
27482         }
27483         
27484         var delegate = this.delegates.shift();
27485         
27486         this.progressDialog.show();
27487         
27488         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27489         
27490         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27491         
27492         delegate();
27493     },
27494     
27495     refresh : function()
27496     {
27497         this.uploader.show();
27498         
27499         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27500             this.uploader.hide();
27501         }
27502         
27503         Roo.isTouch ? this.closable(false) : this.closable(true);
27504         
27505         this.fireEvent('refresh', this);
27506     },
27507     
27508     onRemove : function(e, el, o)
27509     {
27510         e.preventDefault();
27511         
27512         this.fireEvent('remove', this, o);
27513         
27514     },
27515     
27516     remove : function(o)
27517     {
27518         var files = [];
27519         
27520         Roo.each(this.files, function(file){
27521             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27522                 files.push(file);
27523                 return;
27524             }
27525
27526             o.target.remove();
27527
27528         }, this);
27529         
27530         this.files = files;
27531         
27532         this.refresh();
27533     },
27534     
27535     clear : function()
27536     {
27537         Roo.each(this.files, function(file){
27538             if(!file.target){
27539                 return;
27540             }
27541             
27542             file.target.remove();
27543
27544         }, this);
27545         
27546         this.files = [];
27547         
27548         this.refresh();
27549     },
27550     
27551     onClick : function(e, el, o)
27552     {
27553         e.preventDefault();
27554         
27555         this.fireEvent('click', this, o);
27556         
27557     },
27558     
27559     closable : function(closable)
27560     {
27561         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27562             
27563             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27564             
27565             if(closable){
27566                 el.show();
27567                 return;
27568             }
27569             
27570             el.hide();
27571             
27572         }, this);
27573     },
27574     
27575     xhrOnLoad : function(xhr)
27576     {
27577         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27578             el.remove();
27579         }, this);
27580         
27581         if (xhr.readyState !== 4) {
27582             this.arrange();
27583             this.fireEvent('exception', this, xhr);
27584             return;
27585         }
27586
27587         var response = Roo.decode(xhr.responseText);
27588         
27589         if(!response.success){
27590             this.arrange();
27591             this.fireEvent('exception', this, xhr);
27592             return;
27593         }
27594         
27595         var file = this.renderPreview(response.data);
27596         
27597         this.files.push(file);
27598         
27599         this.arrange();
27600         
27601     },
27602     
27603     xhrOnError : function(xhr)
27604     {
27605         Roo.log('xhr on error');
27606         
27607         var response = Roo.decode(xhr.responseText);
27608           
27609         Roo.log(response);
27610         
27611         this.arrange();
27612     },
27613     
27614     process : function(file)
27615     {
27616         if(this.fireEvent('process', this, file) !== false){
27617             if(this.editable && file.type.indexOf('image') != -1){
27618                 this.fireEvent('edit', this, file);
27619                 return;
27620             }
27621
27622             this.uploadStart(file, false);
27623
27624             return;
27625         }
27626         
27627     },
27628     
27629     uploadStart : function(file, crop)
27630     {
27631         this.xhr = new XMLHttpRequest();
27632         
27633         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27634             this.arrange();
27635             return;
27636         }
27637         
27638         file.xhr = this.xhr;
27639             
27640         this.managerEl.createChild({
27641             tag : 'div',
27642             cls : 'roo-document-manager-loading',
27643             cn : [
27644                 {
27645                     tag : 'div',
27646                     tooltip : file.name,
27647                     cls : 'roo-document-manager-thumb',
27648                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27649                 }
27650             ]
27651
27652         });
27653
27654         this.xhr.open(this.method, this.url, true);
27655         
27656         var headers = {
27657             "Accept": "application/json",
27658             "Cache-Control": "no-cache",
27659             "X-Requested-With": "XMLHttpRequest"
27660         };
27661         
27662         for (var headerName in headers) {
27663             var headerValue = headers[headerName];
27664             if (headerValue) {
27665                 this.xhr.setRequestHeader(headerName, headerValue);
27666             }
27667         }
27668         
27669         var _this = this;
27670         
27671         this.xhr.onload = function()
27672         {
27673             _this.xhrOnLoad(_this.xhr);
27674         }
27675         
27676         this.xhr.onerror = function()
27677         {
27678             _this.xhrOnError(_this.xhr);
27679         }
27680         
27681         var formData = new FormData();
27682
27683         formData.append('returnHTML', 'NO');
27684         
27685         if(crop){
27686             formData.append('crop', crop);
27687         }
27688         
27689         formData.append(this.paramName, file, file.name);
27690         
27691         if(this.fireEvent('prepare', this, formData) != false){
27692             this.xhr.send(formData);
27693         };
27694     },
27695     
27696     uploadCancel : function()
27697     {
27698         if (this.xhr) {
27699             this.xhr.abort();
27700         }
27701         
27702         
27703         this.delegates = [];
27704         
27705         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27706             el.remove();
27707         }, this);
27708         
27709         this.arrange();
27710     },
27711     
27712     renderPreview : function(file)
27713     {
27714         if(typeof(file.target) != 'undefined' && file.target){
27715             return file;
27716         }
27717         
27718         var previewEl = this.managerEl.createChild({
27719             tag : 'div',
27720             cls : 'roo-document-manager-preview',
27721             cn : [
27722                 {
27723                     tag : 'div',
27724                     tooltip : file.filename,
27725                     cls : 'roo-document-manager-thumb',
27726                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27727                 },
27728                 {
27729                     tag : 'button',
27730                     cls : 'close',
27731                     html : '<i class="fa fa-times-circle"></i>'
27732                 }
27733             ]
27734         });
27735
27736         var close = previewEl.select('button.close', true).first();
27737
27738         close.on('click', this.onRemove, this, file);
27739
27740         file.target = previewEl;
27741
27742         var image = previewEl.select('img', true).first();
27743         
27744         var _this = this;
27745         
27746         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27747         
27748         image.on('click', this.onClick, this, file);
27749         
27750         return file;
27751         
27752     },
27753     
27754     onPreviewLoad : function(file, image)
27755     {
27756         if(typeof(file.target) == 'undefined' || !file.target){
27757             return;
27758         }
27759         
27760         var width = image.dom.naturalWidth || image.dom.width;
27761         var height = image.dom.naturalHeight || image.dom.height;
27762         
27763         if(width > height){
27764             file.target.addClass('wide');
27765             return;
27766         }
27767         
27768         file.target.addClass('tall');
27769         return;
27770         
27771     },
27772     
27773     uploadFromSource : function(file, crop)
27774     {
27775         this.xhr = new XMLHttpRequest();
27776         
27777         this.managerEl.createChild({
27778             tag : 'div',
27779             cls : 'roo-document-manager-loading',
27780             cn : [
27781                 {
27782                     tag : 'div',
27783                     tooltip : file.name,
27784                     cls : 'roo-document-manager-thumb',
27785                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27786                 }
27787             ]
27788
27789         });
27790
27791         this.xhr.open(this.method, this.url, true);
27792         
27793         var headers = {
27794             "Accept": "application/json",
27795             "Cache-Control": "no-cache",
27796             "X-Requested-With": "XMLHttpRequest"
27797         };
27798         
27799         for (var headerName in headers) {
27800             var headerValue = headers[headerName];
27801             if (headerValue) {
27802                 this.xhr.setRequestHeader(headerName, headerValue);
27803             }
27804         }
27805         
27806         var _this = this;
27807         
27808         this.xhr.onload = function()
27809         {
27810             _this.xhrOnLoad(_this.xhr);
27811         }
27812         
27813         this.xhr.onerror = function()
27814         {
27815             _this.xhrOnError(_this.xhr);
27816         }
27817         
27818         var formData = new FormData();
27819
27820         formData.append('returnHTML', 'NO');
27821         
27822         formData.append('crop', crop);
27823         
27824         if(typeof(file.filename) != 'undefined'){
27825             formData.append('filename', file.filename);
27826         }
27827         
27828         if(typeof(file.mimetype) != 'undefined'){
27829             formData.append('mimetype', file.mimetype);
27830         }
27831         
27832         if(this.fireEvent('prepare', this, formData) != false){
27833             this.xhr.send(formData);
27834         };
27835     }
27836 });
27837
27838 /*
27839 * Licence: LGPL
27840 */
27841
27842 /**
27843  * @class Roo.bootstrap.DocumentViewer
27844  * @extends Roo.bootstrap.Component
27845  * Bootstrap DocumentViewer class
27846  * 
27847  * @constructor
27848  * Create a new DocumentViewer
27849  * @param {Object} config The config object
27850  */
27851
27852 Roo.bootstrap.DocumentViewer = function(config){
27853     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27854     
27855     this.addEvents({
27856         /**
27857          * @event initial
27858          * Fire after initEvent
27859          * @param {Roo.bootstrap.DocumentViewer} this
27860          */
27861         "initial" : true,
27862         /**
27863          * @event click
27864          * Fire after click
27865          * @param {Roo.bootstrap.DocumentViewer} this
27866          */
27867         "click" : true,
27868         /**
27869          * @event trash
27870          * Fire after trash button
27871          * @param {Roo.bootstrap.DocumentViewer} this
27872          */
27873         "trash" : true
27874         
27875     });
27876 };
27877
27878 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27879     
27880     getAutoCreate : function()
27881     {
27882         var cfg = {
27883             tag : 'div',
27884             cls : 'roo-document-viewer',
27885             cn : [
27886                 {
27887                     tag : 'div',
27888                     cls : 'roo-document-viewer-body',
27889                     cn : [
27890                         {
27891                             tag : 'div',
27892                             cls : 'roo-document-viewer-thumb',
27893                             cn : [
27894                                 {
27895                                     tag : 'img',
27896                                     cls : 'roo-document-viewer-image'
27897                                 }
27898                             ]
27899                         }
27900                     ]
27901                 },
27902                 {
27903                     tag : 'div',
27904                     cls : 'roo-document-viewer-footer',
27905                     cn : {
27906                         tag : 'div',
27907                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27908                         cn : [
27909                             {
27910                                 tag : 'div',
27911                                 cls : 'btn-group',
27912                                 cn : [
27913                                     {
27914                                         tag : 'button',
27915                                         cls : 'btn btn-default roo-document-viewer-trash',
27916                                         html : '<i class="fa fa-trash"></i>'
27917                                     }
27918                                 ]
27919                             }
27920                         ]
27921                     }
27922                 }
27923             ]
27924         };
27925         
27926         return cfg;
27927     },
27928     
27929     initEvents : function()
27930     {
27931         
27932         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27933         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27934         
27935         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27936         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27937         
27938         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27939         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27940         
27941         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27942         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27943         
27944         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27945         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27946         
27947         this.bodyEl.on('click', this.onClick, this);
27948         
27949         this.trashBtn.on('click', this.onTrash, this);
27950         
27951     },
27952     
27953     initial : function()
27954     {
27955 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27956         
27957         
27958         this.fireEvent('initial', this);
27959         
27960     },
27961     
27962     onClick : function(e)
27963     {
27964         e.preventDefault();
27965         
27966         this.fireEvent('click', this);
27967     },
27968     
27969     onTrash : function(e)
27970     {
27971         e.preventDefault();
27972         
27973         this.fireEvent('trash', this);
27974     }
27975     
27976 });
27977 /*
27978  * - LGPL
27979  *
27980  * nav progress bar
27981  * 
27982  */
27983
27984 /**
27985  * @class Roo.bootstrap.NavProgressBar
27986  * @extends Roo.bootstrap.Component
27987  * Bootstrap NavProgressBar class
27988  * 
27989  * @constructor
27990  * Create a new nav progress bar
27991  * @param {Object} config The config object
27992  */
27993
27994 Roo.bootstrap.NavProgressBar = function(config){
27995     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27996
27997     this.bullets = this.bullets || [];
27998    
27999 //    Roo.bootstrap.NavProgressBar.register(this);
28000      this.addEvents({
28001         /**
28002              * @event changed
28003              * Fires when the active item changes
28004              * @param {Roo.bootstrap.NavProgressBar} this
28005              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28006              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28007          */
28008         'changed': true
28009      });
28010     
28011 };
28012
28013 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28014     
28015     bullets : [],
28016     barItems : [],
28017     
28018     getAutoCreate : function()
28019     {
28020         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28021         
28022         cfg = {
28023             tag : 'div',
28024             cls : 'roo-navigation-bar-group',
28025             cn : [
28026                 {
28027                     tag : 'div',
28028                     cls : 'roo-navigation-top-bar'
28029                 },
28030                 {
28031                     tag : 'div',
28032                     cls : 'roo-navigation-bullets-bar',
28033                     cn : [
28034                         {
28035                             tag : 'ul',
28036                             cls : 'roo-navigation-bar'
28037                         }
28038                     ]
28039                 },
28040                 
28041                 {
28042                     tag : 'div',
28043                     cls : 'roo-navigation-bottom-bar'
28044                 }
28045             ]
28046             
28047         };
28048         
28049         return cfg;
28050         
28051     },
28052     
28053     initEvents: function() 
28054     {
28055         
28056     },
28057     
28058     onRender : function(ct, position) 
28059     {
28060         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28061         
28062         if(this.bullets.length){
28063             Roo.each(this.bullets, function(b){
28064                this.addItem(b);
28065             }, this);
28066         }
28067         
28068         this.format();
28069         
28070     },
28071     
28072     addItem : function(cfg)
28073     {
28074         var item = new Roo.bootstrap.NavProgressItem(cfg);
28075         
28076         item.parentId = this.id;
28077         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28078         
28079         if(cfg.html){
28080             var top = new Roo.bootstrap.Element({
28081                 tag : 'div',
28082                 cls : 'roo-navigation-bar-text'
28083             });
28084             
28085             var bottom = new Roo.bootstrap.Element({
28086                 tag : 'div',
28087                 cls : 'roo-navigation-bar-text'
28088             });
28089             
28090             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28091             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28092             
28093             var topText = new Roo.bootstrap.Element({
28094                 tag : 'span',
28095                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28096             });
28097             
28098             var bottomText = new Roo.bootstrap.Element({
28099                 tag : 'span',
28100                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28101             });
28102             
28103             topText.onRender(top.el, null);
28104             bottomText.onRender(bottom.el, null);
28105             
28106             item.topEl = top;
28107             item.bottomEl = bottom;
28108         }
28109         
28110         this.barItems.push(item);
28111         
28112         return item;
28113     },
28114     
28115     getActive : function()
28116     {
28117         var active = false;
28118         
28119         Roo.each(this.barItems, function(v){
28120             
28121             if (!v.isActive()) {
28122                 return;
28123             }
28124             
28125             active = v;
28126             return false;
28127             
28128         });
28129         
28130         return active;
28131     },
28132     
28133     setActiveItem : function(item)
28134     {
28135         var prev = false;
28136         
28137         Roo.each(this.barItems, function(v){
28138             if (v.rid == item.rid) {
28139                 return ;
28140             }
28141             
28142             if (v.isActive()) {
28143                 v.setActive(false);
28144                 prev = v;
28145             }
28146         });
28147
28148         item.setActive(true);
28149         
28150         this.fireEvent('changed', this, item, prev);
28151     },
28152     
28153     getBarItem: function(rid)
28154     {
28155         var ret = false;
28156         
28157         Roo.each(this.barItems, function(e) {
28158             if (e.rid != rid) {
28159                 return;
28160             }
28161             
28162             ret =  e;
28163             return false;
28164         });
28165         
28166         return ret;
28167     },
28168     
28169     indexOfItem : function(item)
28170     {
28171         var index = false;
28172         
28173         Roo.each(this.barItems, function(v, i){
28174             
28175             if (v.rid != item.rid) {
28176                 return;
28177             }
28178             
28179             index = i;
28180             return false
28181         });
28182         
28183         return index;
28184     },
28185     
28186     setActiveNext : function()
28187     {
28188         var i = this.indexOfItem(this.getActive());
28189         
28190         if (i > this.barItems.length) {
28191             return;
28192         }
28193         
28194         this.setActiveItem(this.barItems[i+1]);
28195     },
28196     
28197     setActivePrev : function()
28198     {
28199         var i = this.indexOfItem(this.getActive());
28200         
28201         if (i  < 1) {
28202             return;
28203         }
28204         
28205         this.setActiveItem(this.barItems[i-1]);
28206     },
28207     
28208     format : function()
28209     {
28210         if(!this.barItems.length){
28211             return;
28212         }
28213      
28214         var width = 100 / this.barItems.length;
28215         
28216         Roo.each(this.barItems, function(i){
28217             i.el.setStyle('width', width + '%');
28218             i.topEl.el.setStyle('width', width + '%');
28219             i.bottomEl.el.setStyle('width', width + '%');
28220         }, this);
28221         
28222     }
28223     
28224 });
28225 /*
28226  * - LGPL
28227  *
28228  * Nav Progress Item
28229  * 
28230  */
28231
28232 /**
28233  * @class Roo.bootstrap.NavProgressItem
28234  * @extends Roo.bootstrap.Component
28235  * Bootstrap NavProgressItem class
28236  * @cfg {String} rid the reference id
28237  * @cfg {Boolean} active (true|false) Is item active default false
28238  * @cfg {Boolean} disabled (true|false) Is item active default false
28239  * @cfg {String} html
28240  * @cfg {String} position (top|bottom) text position default bottom
28241  * @cfg {String} icon show icon instead of number
28242  * 
28243  * @constructor
28244  * Create a new NavProgressItem
28245  * @param {Object} config The config object
28246  */
28247 Roo.bootstrap.NavProgressItem = function(config){
28248     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28249     this.addEvents({
28250         // raw events
28251         /**
28252          * @event click
28253          * The raw click event for the entire grid.
28254          * @param {Roo.bootstrap.NavProgressItem} this
28255          * @param {Roo.EventObject} e
28256          */
28257         "click" : true
28258     });
28259    
28260 };
28261
28262 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28263     
28264     rid : '',
28265     active : false,
28266     disabled : false,
28267     html : '',
28268     position : 'bottom',
28269     icon : false,
28270     
28271     getAutoCreate : function()
28272     {
28273         var iconCls = 'roo-navigation-bar-item-icon';
28274         
28275         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28276         
28277         var cfg = {
28278             tag: 'li',
28279             cls: 'roo-navigation-bar-item',
28280             cn : [
28281                 {
28282                     tag : 'i',
28283                     cls : iconCls
28284                 }
28285             ]
28286         };
28287         
28288         if(this.active){
28289             cfg.cls += ' active';
28290         }
28291         if(this.disabled){
28292             cfg.cls += ' disabled';
28293         }
28294         
28295         return cfg;
28296     },
28297     
28298     disable : function()
28299     {
28300         this.setDisabled(true);
28301     },
28302     
28303     enable : function()
28304     {
28305         this.setDisabled(false);
28306     },
28307     
28308     initEvents: function() 
28309     {
28310         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28311         
28312         this.iconEl.on('click', this.onClick, this);
28313     },
28314     
28315     onClick : function(e)
28316     {
28317         e.preventDefault();
28318         
28319         if(this.disabled){
28320             return;
28321         }
28322         
28323         if(this.fireEvent('click', this, e) === false){
28324             return;
28325         };
28326         
28327         this.parent().setActiveItem(this);
28328     },
28329     
28330     isActive: function () 
28331     {
28332         return this.active;
28333     },
28334     
28335     setActive : function(state)
28336     {
28337         if(this.active == state){
28338             return;
28339         }
28340         
28341         this.active = state;
28342         
28343         if (state) {
28344             this.el.addClass('active');
28345             return;
28346         }
28347         
28348         this.el.removeClass('active');
28349         
28350         return;
28351     },
28352     
28353     setDisabled : function(state)
28354     {
28355         if(this.disabled == state){
28356             return;
28357         }
28358         
28359         this.disabled = state;
28360         
28361         if (state) {
28362             this.el.addClass('disabled');
28363             return;
28364         }
28365         
28366         this.el.removeClass('disabled');
28367     },
28368     
28369     tooltipEl : function()
28370     {
28371         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28372     }
28373 });
28374  
28375
28376  /*
28377  * - LGPL
28378  *
28379  * FieldLabel
28380  * 
28381  */
28382
28383 /**
28384  * @class Roo.bootstrap.FieldLabel
28385  * @extends Roo.bootstrap.Component
28386  * Bootstrap FieldLabel class
28387  * @cfg {String} html contents of the element
28388  * @cfg {String} tag tag of the element default label
28389  * @cfg {String} cls class of the element
28390  * @cfg {String} target label target 
28391  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28392  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28393  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28394  * @cfg {String} iconTooltip default "This field is required"
28395  * 
28396  * @constructor
28397  * Create a new FieldLabel
28398  * @param {Object} config The config object
28399  */
28400
28401 Roo.bootstrap.FieldLabel = function(config){
28402     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28403     
28404     this.addEvents({
28405             /**
28406              * @event invalid
28407              * Fires after the field has been marked as invalid.
28408              * @param {Roo.form.FieldLabel} this
28409              * @param {String} msg The validation message
28410              */
28411             invalid : true,
28412             /**
28413              * @event valid
28414              * Fires after the field has been validated with no errors.
28415              * @param {Roo.form.FieldLabel} this
28416              */
28417             valid : true
28418         });
28419 };
28420
28421 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28422     
28423     tag: 'label',
28424     cls: '',
28425     html: '',
28426     target: '',
28427     allowBlank : true,
28428     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28429     validClass : 'text-success fa fa-lg fa-check',
28430     iconTooltip : 'This field is required',
28431     
28432     getAutoCreate : function(){
28433         
28434         var cfg = {
28435             tag : this.tag,
28436             cls : 'roo-bootstrap-field-label ' + this.cls,
28437             for : this.target,
28438             cn : [
28439                 {
28440                     tag : 'i',
28441                     cls : '',
28442                     tooltip : this.iconTooltip
28443                 },
28444                 {
28445                     tag : 'span',
28446                     html : this.html
28447                 }
28448             ] 
28449         };
28450         
28451         return cfg;
28452     },
28453     
28454     initEvents: function() 
28455     {
28456         Roo.bootstrap.Element.superclass.initEvents.call(this);
28457         
28458         this.iconEl = this.el.select('i', true).first();
28459         
28460         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28461         
28462         Roo.bootstrap.FieldLabel.register(this);
28463     },
28464     
28465     /**
28466      * Mark this field as valid
28467      */
28468     markValid : function()
28469     {
28470         this.iconEl.show();
28471         
28472         this.iconEl.removeClass(this.invalidClass);
28473         
28474         this.iconEl.addClass(this.validClass);
28475         
28476         this.fireEvent('valid', this);
28477     },
28478     
28479     /**
28480      * Mark this field as invalid
28481      * @param {String} msg The validation message
28482      */
28483     markInvalid : function(msg)
28484     {
28485         this.iconEl.show();
28486         
28487         this.iconEl.removeClass(this.validClass);
28488         
28489         this.iconEl.addClass(this.invalidClass);
28490         
28491         this.fireEvent('invalid', this, msg);
28492     }
28493     
28494    
28495 });
28496
28497 Roo.apply(Roo.bootstrap.FieldLabel, {
28498     
28499     groups: {},
28500     
28501      /**
28502     * register a FieldLabel Group
28503     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28504     */
28505     register : function(label)
28506     {
28507         if(this.groups.hasOwnProperty(label.target)){
28508             return;
28509         }
28510      
28511         this.groups[label.target] = label;
28512         
28513     },
28514     /**
28515     * fetch a FieldLabel Group based on the target
28516     * @param {string} target
28517     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28518     */
28519     get: function(target) {
28520         if (typeof(this.groups[target]) == 'undefined') {
28521             return false;
28522         }
28523         
28524         return this.groups[target] ;
28525     }
28526 });
28527
28528  
28529
28530  /*
28531  * - LGPL
28532  *
28533  * page DateSplitField.
28534  * 
28535  */
28536
28537
28538 /**
28539  * @class Roo.bootstrap.DateSplitField
28540  * @extends Roo.bootstrap.Component
28541  * Bootstrap DateSplitField class
28542  * @cfg {string} fieldLabel - the label associated
28543  * @cfg {Number} labelWidth set the width of label (0-12)
28544  * @cfg {String} labelAlign (top|left)
28545  * @cfg {Boolean} dayAllowBlank (true|false) default false
28546  * @cfg {Boolean} monthAllowBlank (true|false) default false
28547  * @cfg {Boolean} yearAllowBlank (true|false) default false
28548  * @cfg {string} dayPlaceholder 
28549  * @cfg {string} monthPlaceholder
28550  * @cfg {string} yearPlaceholder
28551  * @cfg {string} dayFormat default 'd'
28552  * @cfg {string} monthFormat default 'm'
28553  * @cfg {string} yearFormat default 'Y'
28554
28555  *     
28556  * @constructor
28557  * Create a new DateSplitField
28558  * @param {Object} config The config object
28559  */
28560
28561 Roo.bootstrap.DateSplitField = function(config){
28562     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28563     
28564     this.addEvents({
28565         // raw events
28566          /**
28567          * @event years
28568          * getting the data of years
28569          * @param {Roo.bootstrap.DateSplitField} this
28570          * @param {Object} years
28571          */
28572         "years" : true,
28573         /**
28574          * @event days
28575          * getting the data of days
28576          * @param {Roo.bootstrap.DateSplitField} this
28577          * @param {Object} days
28578          */
28579         "days" : true,
28580         /**
28581          * @event invalid
28582          * Fires after the field has been marked as invalid.
28583          * @param {Roo.form.Field} this
28584          * @param {String} msg The validation message
28585          */
28586         invalid : true,
28587        /**
28588          * @event valid
28589          * Fires after the field has been validated with no errors.
28590          * @param {Roo.form.Field} this
28591          */
28592         valid : true
28593     });
28594 };
28595
28596 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28597     
28598     fieldLabel : '',
28599     labelAlign : 'top',
28600     labelWidth : 3,
28601     dayAllowBlank : false,
28602     monthAllowBlank : false,
28603     yearAllowBlank : false,
28604     dayPlaceholder : '',
28605     monthPlaceholder : '',
28606     yearPlaceholder : '',
28607     dayFormat : 'd',
28608     monthFormat : 'm',
28609     yearFormat : 'Y',
28610     isFormField : true,
28611     
28612     getAutoCreate : function()
28613     {
28614         var cfg = {
28615             tag : 'div',
28616             cls : 'row roo-date-split-field-group',
28617             cn : [
28618                 {
28619                     tag : 'input',
28620                     type : 'hidden',
28621                     cls : 'form-hidden-field roo-date-split-field-group-value',
28622                     name : this.name
28623                 }
28624             ]
28625         };
28626         
28627         if(this.fieldLabel){
28628             cfg.cn.push({
28629                 tag : 'div',
28630                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28631                 cn : [
28632                     {
28633                         tag : 'label',
28634                         html : this.fieldLabel
28635                     }
28636                 ]
28637             });
28638         }
28639         
28640         Roo.each(['day', 'month', 'year'], function(t){
28641             cfg.cn.push({
28642                 tag : 'div',
28643                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28644             });
28645         }, this);
28646         
28647         return cfg;
28648     },
28649     
28650     inputEl: function ()
28651     {
28652         return this.el.select('.roo-date-split-field-group-value', true).first();
28653     },
28654     
28655     onRender : function(ct, position) 
28656     {
28657         var _this = this;
28658         
28659         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28660         
28661         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28662         
28663         this.dayField = new Roo.bootstrap.ComboBox({
28664             allowBlank : this.dayAllowBlank,
28665             alwaysQuery : true,
28666             displayField : 'value',
28667             editable : false,
28668             fieldLabel : '',
28669             forceSelection : true,
28670             mode : 'local',
28671             placeholder : this.dayPlaceholder,
28672             selectOnFocus : true,
28673             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28674             triggerAction : 'all',
28675             typeAhead : true,
28676             valueField : 'value',
28677             store : new Roo.data.SimpleStore({
28678                 data : (function() {    
28679                     var days = [];
28680                     _this.fireEvent('days', _this, days);
28681                     return days;
28682                 })(),
28683                 fields : [ 'value' ]
28684             }),
28685             listeners : {
28686                 select : function (_self, record, index)
28687                 {
28688                     _this.setValue(_this.getValue());
28689                 }
28690             }
28691         });
28692
28693         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28694         
28695         this.monthField = new Roo.bootstrap.MonthField({
28696             after : '<i class=\"fa fa-calendar\"></i>',
28697             allowBlank : this.monthAllowBlank,
28698             placeholder : this.monthPlaceholder,
28699             readOnly : true,
28700             listeners : {
28701                 render : function (_self)
28702                 {
28703                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28704                         e.preventDefault();
28705                         _self.focus();
28706                     });
28707                 },
28708                 select : function (_self, oldvalue, newvalue)
28709                 {
28710                     _this.setValue(_this.getValue());
28711                 }
28712             }
28713         });
28714         
28715         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28716         
28717         this.yearField = new Roo.bootstrap.ComboBox({
28718             allowBlank : this.yearAllowBlank,
28719             alwaysQuery : true,
28720             displayField : 'value',
28721             editable : false,
28722             fieldLabel : '',
28723             forceSelection : true,
28724             mode : 'local',
28725             placeholder : this.yearPlaceholder,
28726             selectOnFocus : true,
28727             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28728             triggerAction : 'all',
28729             typeAhead : true,
28730             valueField : 'value',
28731             store : new Roo.data.SimpleStore({
28732                 data : (function() {
28733                     var years = [];
28734                     _this.fireEvent('years', _this, years);
28735                     return years;
28736                 })(),
28737                 fields : [ 'value' ]
28738             }),
28739             listeners : {
28740                 select : function (_self, record, index)
28741                 {
28742                     _this.setValue(_this.getValue());
28743                 }
28744             }
28745         });
28746
28747         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28748     },
28749     
28750     setValue : function(v, format)
28751     {
28752         this.inputEl.dom.value = v;
28753         
28754         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28755         
28756         var d = Date.parseDate(v, f);
28757         
28758         if(!d){
28759             this.validate();
28760             return;
28761         }
28762         
28763         this.setDay(d.format(this.dayFormat));
28764         this.setMonth(d.format(this.monthFormat));
28765         this.setYear(d.format(this.yearFormat));
28766         
28767         this.validate();
28768         
28769         return;
28770     },
28771     
28772     setDay : function(v)
28773     {
28774         this.dayField.setValue(v);
28775         this.inputEl.dom.value = this.getValue();
28776         this.validate();
28777         return;
28778     },
28779     
28780     setMonth : function(v)
28781     {
28782         this.monthField.setValue(v, true);
28783         this.inputEl.dom.value = this.getValue();
28784         this.validate();
28785         return;
28786     },
28787     
28788     setYear : function(v)
28789     {
28790         this.yearField.setValue(v);
28791         this.inputEl.dom.value = this.getValue();
28792         this.validate();
28793         return;
28794     },
28795     
28796     getDay : function()
28797     {
28798         return this.dayField.getValue();
28799     },
28800     
28801     getMonth : function()
28802     {
28803         return this.monthField.getValue();
28804     },
28805     
28806     getYear : function()
28807     {
28808         return this.yearField.getValue();
28809     },
28810     
28811     getValue : function()
28812     {
28813         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28814         
28815         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28816         
28817         return date;
28818     },
28819     
28820     reset : function()
28821     {
28822         this.setDay('');
28823         this.setMonth('');
28824         this.setYear('');
28825         this.inputEl.dom.value = '';
28826         this.validate();
28827         return;
28828     },
28829     
28830     validate : function()
28831     {
28832         var d = this.dayField.validate();
28833         var m = this.monthField.validate();
28834         var y = this.yearField.validate();
28835         
28836         var valid = true;
28837         
28838         if(
28839                 (!this.dayAllowBlank && !d) ||
28840                 (!this.monthAllowBlank && !m) ||
28841                 (!this.yearAllowBlank && !y)
28842         ){
28843             valid = false;
28844         }
28845         
28846         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28847             return valid;
28848         }
28849         
28850         if(valid){
28851             this.markValid();
28852             return valid;
28853         }
28854         
28855         this.markInvalid();
28856         
28857         return valid;
28858     },
28859     
28860     markValid : function()
28861     {
28862         
28863         var label = this.el.select('label', true).first();
28864         var icon = this.el.select('i.fa-star', true).first();
28865
28866         if(label && icon){
28867             icon.remove();
28868         }
28869         
28870         this.fireEvent('valid', this);
28871     },
28872     
28873      /**
28874      * Mark this field as invalid
28875      * @param {String} msg The validation message
28876      */
28877     markInvalid : function(msg)
28878     {
28879         
28880         var label = this.el.select('label', true).first();
28881         var icon = this.el.select('i.fa-star', true).first();
28882
28883         if(label && !icon){
28884             this.el.select('.roo-date-split-field-label', true).createChild({
28885                 tag : 'i',
28886                 cls : 'text-danger fa fa-lg fa-star',
28887                 tooltip : 'This field is required',
28888                 style : 'margin-right:5px;'
28889             }, label, true);
28890         }
28891         
28892         this.fireEvent('invalid', this, msg);
28893     },
28894     
28895     clearInvalid : function()
28896     {
28897         var label = this.el.select('label', true).first();
28898         var icon = this.el.select('i.fa-star', true).first();
28899
28900         if(label && icon){
28901             icon.remove();
28902         }
28903         
28904         this.fireEvent('valid', this);
28905     },
28906     
28907     getName: function()
28908     {
28909         return this.name;
28910     }
28911     
28912 });
28913
28914  /**
28915  *
28916  * This is based on 
28917  * http://masonry.desandro.com
28918  *
28919  * The idea is to render all the bricks based on vertical width...
28920  *
28921  * The original code extends 'outlayer' - we might need to use that....
28922  * 
28923  */
28924
28925
28926 /**
28927  * @class Roo.bootstrap.LayoutMasonry
28928  * @extends Roo.bootstrap.Component
28929  * Bootstrap Layout Masonry class
28930  * 
28931  * @constructor
28932  * Create a new Element
28933  * @param {Object} config The config object
28934  */
28935
28936 Roo.bootstrap.LayoutMasonry = function(config){
28937     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28938     
28939     this.bricks = [];
28940     
28941 };
28942
28943 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28944     
28945     /**
28946      * @cfg {Boolean} isLayoutInstant = no animation?
28947      */   
28948     isLayoutInstant : false, // needed?
28949    
28950     /**
28951      * @cfg {Number} boxWidth  width of the columns
28952      */   
28953     boxWidth : 450,
28954     
28955       /**
28956      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28957      */   
28958     boxHeight : 0,
28959     
28960     /**
28961      * @cfg {Number} padWidth padding below box..
28962      */   
28963     padWidth : 10, 
28964     
28965     /**
28966      * @cfg {Number} gutter gutter width..
28967      */   
28968     gutter : 10,
28969     
28970      /**
28971      * @cfg {Number} maxCols maximum number of columns
28972      */   
28973     
28974     maxCols: 0,
28975     
28976     /**
28977      * @cfg {Boolean} isAutoInitial defalut true
28978      */   
28979     isAutoInitial : true, 
28980     
28981     containerWidth: 0,
28982     
28983     /**
28984      * @cfg {Boolean} isHorizontal defalut false
28985      */   
28986     isHorizontal : false, 
28987
28988     currentSize : null,
28989     
28990     tag: 'div',
28991     
28992     cls: '',
28993     
28994     bricks: null, //CompositeElement
28995     
28996     cols : 1,
28997     
28998     _isLayoutInited : false,
28999     
29000 //    isAlternative : false, // only use for vertical layout...
29001     
29002     /**
29003      * @cfg {Number} alternativePadWidth padding below box..
29004      */   
29005     alternativePadWidth : 50, 
29006     
29007     getAutoCreate : function(){
29008         
29009         var cfg = {
29010             tag: this.tag,
29011             cls: 'blog-masonary-wrapper ' + this.cls,
29012             cn : {
29013                 cls : 'mas-boxes masonary'
29014             }
29015         };
29016         
29017         return cfg;
29018     },
29019     
29020     getChildContainer: function( )
29021     {
29022         if (this.boxesEl) {
29023             return this.boxesEl;
29024         }
29025         
29026         this.boxesEl = this.el.select('.mas-boxes').first();
29027         
29028         return this.boxesEl;
29029     },
29030     
29031     
29032     initEvents : function()
29033     {
29034         var _this = this;
29035         
29036         if(this.isAutoInitial){
29037             Roo.log('hook children rendered');
29038             this.on('childrenrendered', function() {
29039                 Roo.log('children rendered');
29040                 _this.initial();
29041             } ,this);
29042         }
29043     },
29044     
29045     initial : function()
29046     {
29047         this.currentSize = this.el.getBox(true);
29048         
29049         Roo.EventManager.onWindowResize(this.resize, this); 
29050
29051         if(!this.isAutoInitial){
29052             this.layout();
29053             return;
29054         }
29055         
29056         this.layout();
29057         
29058         return;
29059         //this.layout.defer(500,this);
29060         
29061     },
29062     
29063     resize : function()
29064     {
29065         Roo.log('resize');
29066         
29067         var cs = this.el.getBox(true);
29068         
29069         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29070             Roo.log("no change in with or X");
29071             return;
29072         }
29073         
29074         this.currentSize = cs;
29075         
29076         this.layout();
29077         
29078     },
29079     
29080     layout : function()
29081     {   
29082         this._resetLayout();
29083         
29084         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29085         
29086         this.layoutItems( isInstant );
29087       
29088         this._isLayoutInited = true;
29089         
29090     },
29091     
29092     _resetLayout : function()
29093     {
29094         if(this.isHorizontal){
29095             this.horizontalMeasureColumns();
29096             return;
29097         }
29098         
29099         this.verticalMeasureColumns();
29100         
29101     },
29102     
29103     verticalMeasureColumns : function()
29104     {
29105         this.getContainerWidth();
29106         
29107 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29108 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29109 //            return;
29110 //        }
29111         
29112         var boxWidth = this.boxWidth + this.padWidth;
29113         
29114         if(this.containerWidth < this.boxWidth){
29115             boxWidth = this.containerWidth
29116         }
29117         
29118         var containerWidth = this.containerWidth;
29119         
29120         var cols = Math.floor(containerWidth / boxWidth);
29121         
29122         this.cols = Math.max( cols, 1 );
29123         
29124         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29125         
29126         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29127         
29128         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29129         
29130         this.colWidth = boxWidth + avail - this.padWidth;
29131         
29132         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29133         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29134     },
29135     
29136     horizontalMeasureColumns : function()
29137     {
29138         this.getContainerWidth();
29139         
29140         var boxWidth = this.boxWidth;
29141         
29142         if(this.containerWidth < boxWidth){
29143             boxWidth = this.containerWidth;
29144         }
29145         
29146         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29147         
29148         this.el.setHeight(boxWidth);
29149         
29150     },
29151     
29152     getContainerWidth : function()
29153     {
29154         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29155     },
29156     
29157     layoutItems : function( isInstant )
29158     {
29159         var items = Roo.apply([], this.bricks);
29160         
29161         if(this.isHorizontal){
29162             this._horizontalLayoutItems( items , isInstant );
29163             return;
29164         }
29165         
29166 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29167 //            this._verticalAlternativeLayoutItems( items , isInstant );
29168 //            return;
29169 //        }
29170         
29171         this._verticalLayoutItems( items , isInstant );
29172         
29173     },
29174     
29175     _verticalLayoutItems : function ( items , isInstant)
29176     {
29177         if ( !items || !items.length ) {
29178             return;
29179         }
29180         
29181         var standard = [
29182             ['xs', 'xs', 'xs', 'tall'],
29183             ['xs', 'xs', 'tall'],
29184             ['xs', 'xs', 'sm'],
29185             ['xs', 'xs', 'xs'],
29186             ['xs', 'tall'],
29187             ['xs', 'sm'],
29188             ['xs', 'xs'],
29189             ['xs'],
29190             
29191             ['sm', 'xs', 'xs'],
29192             ['sm', 'xs'],
29193             ['sm'],
29194             
29195             ['tall', 'xs', 'xs', 'xs'],
29196             ['tall', 'xs', 'xs'],
29197             ['tall', 'xs'],
29198             ['tall']
29199             
29200         ];
29201         
29202         var queue = [];
29203         
29204         var boxes = [];
29205         
29206         var box = [];
29207         
29208         Roo.each(items, function(item, k){
29209             
29210             switch (item.size) {
29211                 // these layouts take up a full box,
29212                 case 'md' :
29213                 case 'md-left' :
29214                 case 'md-right' :
29215                 case 'wide' :
29216                     
29217                     if(box.length){
29218                         boxes.push(box);
29219                         box = [];
29220                     }
29221                     
29222                     boxes.push([item]);
29223                     
29224                     break;
29225                     
29226                 case 'xs' :
29227                 case 'sm' :
29228                 case 'tall' :
29229                     
29230                     box.push(item);
29231                     
29232                     break;
29233                 default :
29234                     break;
29235                     
29236             }
29237             
29238         }, this);
29239         
29240         if(box.length){
29241             boxes.push(box);
29242             box = [];
29243         }
29244         
29245         var filterPattern = function(box, length)
29246         {
29247             if(!box.length){
29248                 return;
29249             }
29250             
29251             var match = false;
29252             
29253             var pattern = box.slice(0, length);
29254             
29255             var format = [];
29256             
29257             Roo.each(pattern, function(i){
29258                 format.push(i.size);
29259             }, this);
29260             
29261             Roo.each(standard, function(s){
29262                 
29263                 if(String(s) != String(format)){
29264                     return;
29265                 }
29266                 
29267                 match = true;
29268                 return false;
29269                 
29270             }, this);
29271             
29272             if(!match && length == 1){
29273                 return;
29274             }
29275             
29276             if(!match){
29277                 filterPattern(box, length - 1);
29278                 return;
29279             }
29280                 
29281             queue.push(pattern);
29282
29283             box = box.slice(length, box.length);
29284
29285             filterPattern(box, 4);
29286
29287             return;
29288             
29289         }
29290         
29291         Roo.each(boxes, function(box, k){
29292             
29293             if(!box.length){
29294                 return;
29295             }
29296             
29297             if(box.length == 1){
29298                 queue.push(box);
29299                 return;
29300             }
29301             
29302             filterPattern(box, 4);
29303             
29304         }, this);
29305         
29306         this._processVerticalLayoutQueue( queue, isInstant );
29307         
29308     },
29309     
29310 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29311 //    {
29312 //        if ( !items || !items.length ) {
29313 //            return;
29314 //        }
29315 //
29316 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29317 //        
29318 //    },
29319     
29320     _horizontalLayoutItems : function ( items , isInstant)
29321     {
29322         if ( !items || !items.length || items.length < 3) {
29323             return;
29324         }
29325         
29326         items.reverse();
29327         
29328         var eItems = items.slice(0, 3);
29329         
29330         items = items.slice(3, items.length);
29331         
29332         var standard = [
29333             ['xs', 'xs', 'xs', 'wide'],
29334             ['xs', 'xs', 'wide'],
29335             ['xs', 'xs', 'sm'],
29336             ['xs', 'xs', 'xs'],
29337             ['xs', 'wide'],
29338             ['xs', 'sm'],
29339             ['xs', 'xs'],
29340             ['xs'],
29341             
29342             ['sm', 'xs', 'xs'],
29343             ['sm', 'xs'],
29344             ['sm'],
29345             
29346             ['wide', 'xs', 'xs', 'xs'],
29347             ['wide', 'xs', 'xs'],
29348             ['wide', 'xs'],
29349             ['wide'],
29350             
29351             ['wide-thin']
29352         ];
29353         
29354         var queue = [];
29355         
29356         var boxes = [];
29357         
29358         var box = [];
29359         
29360         Roo.each(items, function(item, k){
29361             
29362             switch (item.size) {
29363                 case 'md' :
29364                 case 'md-left' :
29365                 case 'md-right' :
29366                 case 'tall' :
29367                     
29368                     if(box.length){
29369                         boxes.push(box);
29370                         box = [];
29371                     }
29372                     
29373                     boxes.push([item]);
29374                     
29375                     break;
29376                     
29377                 case 'xs' :
29378                 case 'sm' :
29379                 case 'wide' :
29380                 case 'wide-thin' :
29381                     
29382                     box.push(item);
29383                     
29384                     break;
29385                 default :
29386                     break;
29387                     
29388             }
29389             
29390         }, this);
29391         
29392         if(box.length){
29393             boxes.push(box);
29394             box = [];
29395         }
29396         
29397         var filterPattern = function(box, length)
29398         {
29399             if(!box.length){
29400                 return;
29401             }
29402             
29403             var match = false;
29404             
29405             var pattern = box.slice(0, length);
29406             
29407             var format = [];
29408             
29409             Roo.each(pattern, function(i){
29410                 format.push(i.size);
29411             }, this);
29412             
29413             Roo.each(standard, function(s){
29414                 
29415                 if(String(s) != String(format)){
29416                     return;
29417                 }
29418                 
29419                 match = true;
29420                 return false;
29421                 
29422             }, this);
29423             
29424             if(!match && length == 1){
29425                 return;
29426             }
29427             
29428             if(!match){
29429                 filterPattern(box, length - 1);
29430                 return;
29431             }
29432                 
29433             queue.push(pattern);
29434
29435             box = box.slice(length, box.length);
29436
29437             filterPattern(box, 4);
29438
29439             return;
29440             
29441         }
29442         
29443         Roo.each(boxes, function(box, k){
29444             
29445             if(!box.length){
29446                 return;
29447             }
29448             
29449             if(box.length == 1){
29450                 queue.push(box);
29451                 return;
29452             }
29453             
29454             filterPattern(box, 4);
29455             
29456         }, this);
29457         
29458         
29459         var prune = [];
29460         
29461         var pos = this.el.getBox(true);
29462         
29463         var minX = pos.x;
29464         
29465         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29466         
29467         var hit_end = false;
29468         
29469         Roo.each(queue, function(box){
29470             
29471             if(hit_end){
29472                 
29473                 Roo.each(box, function(b){
29474                 
29475                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29476                     b.el.hide();
29477
29478                 }, this);
29479
29480                 return;
29481             }
29482             
29483             var mx = 0;
29484             
29485             Roo.each(box, function(b){
29486                 
29487                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29488                 b.el.show();
29489
29490                 mx = Math.max(mx, b.x);
29491                 
29492             }, this);
29493             
29494             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29495             
29496             if(maxX < minX){
29497                 
29498                 Roo.each(box, function(b){
29499                 
29500                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29501                     b.el.hide();
29502                     
29503                 }, this);
29504                 
29505                 hit_end = true;
29506                 
29507                 return;
29508             }
29509             
29510             prune.push(box);
29511             
29512         }, this);
29513         
29514         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29515     },
29516     
29517     /** Sets position of item in DOM
29518     * @param {Element} item
29519     * @param {Number} x - horizontal position
29520     * @param {Number} y - vertical position
29521     * @param {Boolean} isInstant - disables transitions
29522     */
29523     _processVerticalLayoutQueue : function( queue, isInstant )
29524     {
29525         var pos = this.el.getBox(true);
29526         var x = pos.x;
29527         var y = pos.y;
29528         var maxY = [];
29529         
29530         for (var i = 0; i < this.cols; i++){
29531             maxY[i] = pos.y;
29532         }
29533         
29534         Roo.each(queue, function(box, k){
29535             
29536             var col = k % this.cols;
29537             
29538             Roo.each(box, function(b,kk){
29539                 
29540                 b.el.position('absolute');
29541                 
29542                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29543                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29544                 
29545                 if(b.size == 'md-left' || b.size == 'md-right'){
29546                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29547                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29548                 }
29549                 
29550                 b.el.setWidth(width);
29551                 b.el.setHeight(height);
29552                 // iframe?
29553                 b.el.select('iframe',true).setSize(width,height);
29554                 
29555             }, this);
29556             
29557             for (var i = 0; i < this.cols; i++){
29558                 
29559                 if(maxY[i] < maxY[col]){
29560                     col = i;
29561                     continue;
29562                 }
29563                 
29564                 col = Math.min(col, i);
29565                 
29566             }
29567             
29568             x = pos.x + col * (this.colWidth + this.padWidth);
29569             
29570             y = maxY[col];
29571             
29572             var positions = [];
29573             
29574             switch (box.length){
29575                 case 1 :
29576                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29577                     break;
29578                 case 2 :
29579                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29580                     break;
29581                 case 3 :
29582                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29583                     break;
29584                 case 4 :
29585                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29586                     break;
29587                 default :
29588                     break;
29589             }
29590             
29591             Roo.each(box, function(b,kk){
29592                 
29593                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29594                 
29595                 var sz = b.el.getSize();
29596                 
29597                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29598                 
29599             }, this);
29600             
29601         }, this);
29602         
29603         var mY = 0;
29604         
29605         for (var i = 0; i < this.cols; i++){
29606             mY = Math.max(mY, maxY[i]);
29607         }
29608         
29609         this.el.setHeight(mY - pos.y);
29610         
29611     },
29612     
29613 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29614 //    {
29615 //        var pos = this.el.getBox(true);
29616 //        var x = pos.x;
29617 //        var y = pos.y;
29618 //        var maxX = pos.right;
29619 //        
29620 //        var maxHeight = 0;
29621 //        
29622 //        Roo.each(items, function(item, k){
29623 //            
29624 //            var c = k % 2;
29625 //            
29626 //            item.el.position('absolute');
29627 //                
29628 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29629 //
29630 //            item.el.setWidth(width);
29631 //
29632 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29633 //
29634 //            item.el.setHeight(height);
29635 //            
29636 //            if(c == 0){
29637 //                item.el.setXY([x, y], isInstant ? false : true);
29638 //            } else {
29639 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29640 //            }
29641 //            
29642 //            y = y + height + this.alternativePadWidth;
29643 //            
29644 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29645 //            
29646 //        }, this);
29647 //        
29648 //        this.el.setHeight(maxHeight);
29649 //        
29650 //    },
29651     
29652     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29653     {
29654         var pos = this.el.getBox(true);
29655         
29656         var minX = pos.x;
29657         var minY = pos.y;
29658         
29659         var maxX = pos.right;
29660         
29661         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29662         
29663         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29664         
29665         Roo.each(queue, function(box, k){
29666             
29667             Roo.each(box, function(b, kk){
29668                 
29669                 b.el.position('absolute');
29670                 
29671                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29672                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29673                 
29674                 if(b.size == 'md-left' || b.size == 'md-right'){
29675                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29676                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29677                 }
29678                 
29679                 b.el.setWidth(width);
29680                 b.el.setHeight(height);
29681                 
29682             }, this);
29683             
29684             if(!box.length){
29685                 return;
29686             }
29687             
29688             var positions = [];
29689             
29690             switch (box.length){
29691                 case 1 :
29692                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29693                     break;
29694                 case 2 :
29695                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29696                     break;
29697                 case 3 :
29698                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29699                     break;
29700                 case 4 :
29701                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29702                     break;
29703                 default :
29704                     break;
29705             }
29706             
29707             Roo.each(box, function(b,kk){
29708                 
29709                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29710                 
29711                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29712                 
29713             }, this);
29714             
29715         }, this);
29716         
29717     },
29718     
29719     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29720     {
29721         Roo.each(eItems, function(b,k){
29722             
29723             b.size = (k == 0) ? 'sm' : 'xs';
29724             b.x = (k == 0) ? 2 : 1;
29725             b.y = (k == 0) ? 2 : 1;
29726             
29727             b.el.position('absolute');
29728             
29729             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29730                 
29731             b.el.setWidth(width);
29732             
29733             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29734             
29735             b.el.setHeight(height);
29736             
29737         }, this);
29738
29739         var positions = [];
29740         
29741         positions.push({
29742             x : maxX - this.unitWidth * 2 - this.gutter,
29743             y : minY
29744         });
29745         
29746         positions.push({
29747             x : maxX - this.unitWidth,
29748             y : minY + (this.unitWidth + this.gutter) * 2
29749         });
29750         
29751         positions.push({
29752             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29753             y : minY
29754         });
29755         
29756         Roo.each(eItems, function(b,k){
29757             
29758             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29759
29760         }, this);
29761         
29762     },
29763     
29764     getVerticalOneBoxColPositions : function(x, y, box)
29765     {
29766         var pos = [];
29767         
29768         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29769         
29770         if(box[0].size == 'md-left'){
29771             rand = 0;
29772         }
29773         
29774         if(box[0].size == 'md-right'){
29775             rand = 1;
29776         }
29777         
29778         pos.push({
29779             x : x + (this.unitWidth + this.gutter) * rand,
29780             y : y
29781         });
29782         
29783         return pos;
29784     },
29785     
29786     getVerticalTwoBoxColPositions : function(x, y, box)
29787     {
29788         var pos = [];
29789         
29790         if(box[0].size == 'xs'){
29791             
29792             pos.push({
29793                 x : x,
29794                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29795             });
29796
29797             pos.push({
29798                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29799                 y : y
29800             });
29801             
29802             return pos;
29803             
29804         }
29805         
29806         pos.push({
29807             x : x,
29808             y : y
29809         });
29810
29811         pos.push({
29812             x : x + (this.unitWidth + this.gutter) * 2,
29813             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29814         });
29815         
29816         return pos;
29817         
29818     },
29819     
29820     getVerticalThreeBoxColPositions : function(x, y, box)
29821     {
29822         var pos = [];
29823         
29824         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29825             
29826             pos.push({
29827                 x : x,
29828                 y : y
29829             });
29830
29831             pos.push({
29832                 x : x + (this.unitWidth + this.gutter) * 1,
29833                 y : y
29834             });
29835             
29836             pos.push({
29837                 x : x + (this.unitWidth + this.gutter) * 2,
29838                 y : y
29839             });
29840             
29841             return pos;
29842             
29843         }
29844         
29845         if(box[0].size == 'xs' && box[1].size == 'xs'){
29846             
29847             pos.push({
29848                 x : x,
29849                 y : y
29850             });
29851
29852             pos.push({
29853                 x : x,
29854                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29855             });
29856             
29857             pos.push({
29858                 x : x + (this.unitWidth + this.gutter) * 1,
29859                 y : y
29860             });
29861             
29862             return pos;
29863             
29864         }
29865         
29866         pos.push({
29867             x : x,
29868             y : y
29869         });
29870
29871         pos.push({
29872             x : x + (this.unitWidth + this.gutter) * 2,
29873             y : y
29874         });
29875
29876         pos.push({
29877             x : x + (this.unitWidth + this.gutter) * 2,
29878             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29879         });
29880             
29881         return pos;
29882         
29883     },
29884     
29885     getVerticalFourBoxColPositions : function(x, y, box)
29886     {
29887         var pos = [];
29888         
29889         if(box[0].size == 'xs'){
29890             
29891             pos.push({
29892                 x : x,
29893                 y : y
29894             });
29895
29896             pos.push({
29897                 x : x,
29898                 y : y + (this.unitHeight + this.gutter) * 1
29899             });
29900             
29901             pos.push({
29902                 x : x,
29903                 y : y + (this.unitHeight + this.gutter) * 2
29904             });
29905             
29906             pos.push({
29907                 x : x + (this.unitWidth + this.gutter) * 1,
29908                 y : y
29909             });
29910             
29911             return pos;
29912             
29913         }
29914         
29915         pos.push({
29916             x : x,
29917             y : y
29918         });
29919
29920         pos.push({
29921             x : x + (this.unitWidth + this.gutter) * 2,
29922             y : y
29923         });
29924
29925         pos.push({
29926             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29927             y : y + (this.unitHeight + this.gutter) * 1
29928         });
29929
29930         pos.push({
29931             x : x + (this.unitWidth + this.gutter) * 2,
29932             y : y + (this.unitWidth + this.gutter) * 2
29933         });
29934
29935         return pos;
29936         
29937     },
29938     
29939     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29940     {
29941         var pos = [];
29942         
29943         if(box[0].size == 'md-left'){
29944             pos.push({
29945                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29946                 y : minY
29947             });
29948             
29949             return pos;
29950         }
29951         
29952         if(box[0].size == 'md-right'){
29953             pos.push({
29954                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29955                 y : minY + (this.unitWidth + this.gutter) * 1
29956             });
29957             
29958             return pos;
29959         }
29960         
29961         var rand = Math.floor(Math.random() * (4 - box[0].y));
29962         
29963         pos.push({
29964             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29965             y : minY + (this.unitWidth + this.gutter) * rand
29966         });
29967         
29968         return pos;
29969         
29970     },
29971     
29972     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29973     {
29974         var pos = [];
29975         
29976         if(box[0].size == 'xs'){
29977             
29978             pos.push({
29979                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29980                 y : minY
29981             });
29982
29983             pos.push({
29984                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29985                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29986             });
29987             
29988             return pos;
29989             
29990         }
29991         
29992         pos.push({
29993             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29994             y : minY
29995         });
29996
29997         pos.push({
29998             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29999             y : minY + (this.unitWidth + this.gutter) * 2
30000         });
30001         
30002         return pos;
30003         
30004     },
30005     
30006     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30007     {
30008         var pos = [];
30009         
30010         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30011             
30012             pos.push({
30013                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30014                 y : minY
30015             });
30016
30017             pos.push({
30018                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30019                 y : minY + (this.unitWidth + this.gutter) * 1
30020             });
30021             
30022             pos.push({
30023                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30024                 y : minY + (this.unitWidth + this.gutter) * 2
30025             });
30026             
30027             return pos;
30028             
30029         }
30030         
30031         if(box[0].size == 'xs' && box[1].size == 'xs'){
30032             
30033             pos.push({
30034                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30035                 y : minY
30036             });
30037
30038             pos.push({
30039                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30040                 y : minY
30041             });
30042             
30043             pos.push({
30044                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30045                 y : minY + (this.unitWidth + this.gutter) * 1
30046             });
30047             
30048             return pos;
30049             
30050         }
30051         
30052         pos.push({
30053             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30054             y : minY
30055         });
30056
30057         pos.push({
30058             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30059             y : minY + (this.unitWidth + this.gutter) * 2
30060         });
30061
30062         pos.push({
30063             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30064             y : minY + (this.unitWidth + this.gutter) * 2
30065         });
30066             
30067         return pos;
30068         
30069     },
30070     
30071     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30072     {
30073         var pos = [];
30074         
30075         if(box[0].size == 'xs'){
30076             
30077             pos.push({
30078                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30079                 y : minY
30080             });
30081
30082             pos.push({
30083                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30084                 y : minY
30085             });
30086             
30087             pos.push({
30088                 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),
30089                 y : minY
30090             });
30091             
30092             pos.push({
30093                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30094                 y : minY + (this.unitWidth + this.gutter) * 1
30095             });
30096             
30097             return pos;
30098             
30099         }
30100         
30101         pos.push({
30102             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30103             y : minY
30104         });
30105         
30106         pos.push({
30107             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30108             y : minY + (this.unitWidth + this.gutter) * 2
30109         });
30110         
30111         pos.push({
30112             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30113             y : minY + (this.unitWidth + this.gutter) * 2
30114         });
30115         
30116         pos.push({
30117             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),
30118             y : minY + (this.unitWidth + this.gutter) * 2
30119         });
30120
30121         return pos;
30122         
30123     }
30124     
30125 });
30126
30127  
30128
30129  /**
30130  *
30131  * This is based on 
30132  * http://masonry.desandro.com
30133  *
30134  * The idea is to render all the bricks based on vertical width...
30135  *
30136  * The original code extends 'outlayer' - we might need to use that....
30137  * 
30138  */
30139
30140
30141 /**
30142  * @class Roo.bootstrap.LayoutMasonryAuto
30143  * @extends Roo.bootstrap.Component
30144  * Bootstrap Layout Masonry class
30145  * 
30146  * @constructor
30147  * Create a new Element
30148  * @param {Object} config The config object
30149  */
30150
30151 Roo.bootstrap.LayoutMasonryAuto = function(config){
30152     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30153 };
30154
30155 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30156     
30157       /**
30158      * @cfg {Boolean} isFitWidth  - resize the width..
30159      */   
30160     isFitWidth : false,  // options..
30161     /**
30162      * @cfg {Boolean} isOriginLeft = left align?
30163      */   
30164     isOriginLeft : true,
30165     /**
30166      * @cfg {Boolean} isOriginTop = top align?
30167      */   
30168     isOriginTop : false,
30169     /**
30170      * @cfg {Boolean} isLayoutInstant = no animation?
30171      */   
30172     isLayoutInstant : false, // needed?
30173     /**
30174      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30175      */   
30176     isResizingContainer : true,
30177     /**
30178      * @cfg {Number} columnWidth  width of the columns 
30179      */   
30180     
30181     columnWidth : 0,
30182     
30183     /**
30184      * @cfg {Number} maxCols maximum number of columns
30185      */   
30186     
30187     maxCols: 0,
30188     /**
30189      * @cfg {Number} padHeight padding below box..
30190      */   
30191     
30192     padHeight : 10, 
30193     
30194     /**
30195      * @cfg {Boolean} isAutoInitial defalut true
30196      */   
30197     
30198     isAutoInitial : true, 
30199     
30200     // private?
30201     gutter : 0,
30202     
30203     containerWidth: 0,
30204     initialColumnWidth : 0,
30205     currentSize : null,
30206     
30207     colYs : null, // array.
30208     maxY : 0,
30209     padWidth: 10,
30210     
30211     
30212     tag: 'div',
30213     cls: '',
30214     bricks: null, //CompositeElement
30215     cols : 0, // array?
30216     // element : null, // wrapped now this.el
30217     _isLayoutInited : null, 
30218     
30219     
30220     getAutoCreate : function(){
30221         
30222         var cfg = {
30223             tag: this.tag,
30224             cls: 'blog-masonary-wrapper ' + this.cls,
30225             cn : {
30226                 cls : 'mas-boxes masonary'
30227             }
30228         };
30229         
30230         return cfg;
30231     },
30232     
30233     getChildContainer: function( )
30234     {
30235         if (this.boxesEl) {
30236             return this.boxesEl;
30237         }
30238         
30239         this.boxesEl = this.el.select('.mas-boxes').first();
30240         
30241         return this.boxesEl;
30242     },
30243     
30244     
30245     initEvents : function()
30246     {
30247         var _this = this;
30248         
30249         if(this.isAutoInitial){
30250             Roo.log('hook children rendered');
30251             this.on('childrenrendered', function() {
30252                 Roo.log('children rendered');
30253                 _this.initial();
30254             } ,this);
30255         }
30256         
30257     },
30258     
30259     initial : function()
30260     {
30261         this.reloadItems();
30262
30263         this.currentSize = this.el.getBox(true);
30264
30265         /// was window resize... - let's see if this works..
30266         Roo.EventManager.onWindowResize(this.resize, this); 
30267
30268         if(!this.isAutoInitial){
30269             this.layout();
30270             return;
30271         }
30272         
30273         this.layout.defer(500,this);
30274     },
30275     
30276     reloadItems: function()
30277     {
30278         this.bricks = this.el.select('.masonry-brick', true);
30279         
30280         this.bricks.each(function(b) {
30281             //Roo.log(b.getSize());
30282             if (!b.attr('originalwidth')) {
30283                 b.attr('originalwidth',  b.getSize().width);
30284             }
30285             
30286         });
30287         
30288         Roo.log(this.bricks.elements.length);
30289     },
30290     
30291     resize : function()
30292     {
30293         Roo.log('resize');
30294         var cs = this.el.getBox(true);
30295         
30296         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30297             Roo.log("no change in with or X");
30298             return;
30299         }
30300         this.currentSize = cs;
30301         this.layout();
30302     },
30303     
30304     layout : function()
30305     {
30306          Roo.log('layout');
30307         this._resetLayout();
30308         //this._manageStamps();
30309       
30310         // don't animate first layout
30311         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30312         this.layoutItems( isInstant );
30313       
30314         // flag for initalized
30315         this._isLayoutInited = true;
30316     },
30317     
30318     layoutItems : function( isInstant )
30319     {
30320         //var items = this._getItemsForLayout( this.items );
30321         // original code supports filtering layout items.. we just ignore it..
30322         
30323         this._layoutItems( this.bricks , isInstant );
30324       
30325         this._postLayout();
30326     },
30327     _layoutItems : function ( items , isInstant)
30328     {
30329        //this.fireEvent( 'layout', this, items );
30330     
30331
30332         if ( !items || !items.elements.length ) {
30333           // no items, emit event with empty array
30334             return;
30335         }
30336
30337         var queue = [];
30338         items.each(function(item) {
30339             Roo.log("layout item");
30340             Roo.log(item);
30341             // get x/y object from method
30342             var position = this._getItemLayoutPosition( item );
30343             // enqueue
30344             position.item = item;
30345             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30346             queue.push( position );
30347         }, this);
30348       
30349         this._processLayoutQueue( queue );
30350     },
30351     /** Sets position of item in DOM
30352     * @param {Element} item
30353     * @param {Number} x - horizontal position
30354     * @param {Number} y - vertical position
30355     * @param {Boolean} isInstant - disables transitions
30356     */
30357     _processLayoutQueue : function( queue )
30358     {
30359         for ( var i=0, len = queue.length; i < len; i++ ) {
30360             var obj = queue[i];
30361             obj.item.position('absolute');
30362             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30363         }
30364     },
30365       
30366     
30367     /**
30368     * Any logic you want to do after each layout,
30369     * i.e. size the container
30370     */
30371     _postLayout : function()
30372     {
30373         this.resizeContainer();
30374     },
30375     
30376     resizeContainer : function()
30377     {
30378         if ( !this.isResizingContainer ) {
30379             return;
30380         }
30381         var size = this._getContainerSize();
30382         if ( size ) {
30383             this.el.setSize(size.width,size.height);
30384             this.boxesEl.setSize(size.width,size.height);
30385         }
30386     },
30387     
30388     
30389     
30390     _resetLayout : function()
30391     {
30392         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30393         this.colWidth = this.el.getWidth();
30394         //this.gutter = this.el.getWidth(); 
30395         
30396         this.measureColumns();
30397
30398         // reset column Y
30399         var i = this.cols;
30400         this.colYs = [];
30401         while (i--) {
30402             this.colYs.push( 0 );
30403         }
30404     
30405         this.maxY = 0;
30406     },
30407
30408     measureColumns : function()
30409     {
30410         this.getContainerWidth();
30411       // if columnWidth is 0, default to outerWidth of first item
30412         if ( !this.columnWidth ) {
30413             var firstItem = this.bricks.first();
30414             Roo.log(firstItem);
30415             this.columnWidth  = this.containerWidth;
30416             if (firstItem && firstItem.attr('originalwidth') ) {
30417                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30418             }
30419             // columnWidth fall back to item of first element
30420             Roo.log("set column width?");
30421                         this.initialColumnWidth = this.columnWidth  ;
30422
30423             // if first elem has no width, default to size of container
30424             
30425         }
30426         
30427         
30428         if (this.initialColumnWidth) {
30429             this.columnWidth = this.initialColumnWidth;
30430         }
30431         
30432         
30433             
30434         // column width is fixed at the top - however if container width get's smaller we should
30435         // reduce it...
30436         
30437         // this bit calcs how man columns..
30438             
30439         var columnWidth = this.columnWidth += this.gutter;
30440       
30441         // calculate columns
30442         var containerWidth = this.containerWidth + this.gutter;
30443         
30444         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30445         // fix rounding errors, typically with gutters
30446         var excess = columnWidth - containerWidth % columnWidth;
30447         
30448         
30449         // if overshoot is less than a pixel, round up, otherwise floor it
30450         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30451         cols = Math[ mathMethod ]( cols );
30452         this.cols = Math.max( cols, 1 );
30453         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30454         
30455          // padding positioning..
30456         var totalColWidth = this.cols * this.columnWidth;
30457         var padavail = this.containerWidth - totalColWidth;
30458         // so for 2 columns - we need 3 'pads'
30459         
30460         var padNeeded = (1+this.cols) * this.padWidth;
30461         
30462         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30463         
30464         this.columnWidth += padExtra
30465         //this.padWidth = Math.floor(padavail /  ( this.cols));
30466         
30467         // adjust colum width so that padding is fixed??
30468         
30469         // we have 3 columns ... total = width * 3
30470         // we have X left over... that should be used by 
30471         
30472         //if (this.expandC) {
30473             
30474         //}
30475         
30476         
30477         
30478     },
30479     
30480     getContainerWidth : function()
30481     {
30482        /* // container is parent if fit width
30483         var container = this.isFitWidth ? this.element.parentNode : this.element;
30484         // check that this.size and size are there
30485         // IE8 triggers resize on body size change, so they might not be
30486         
30487         var size = getSize( container );  //FIXME
30488         this.containerWidth = size && size.innerWidth; //FIXME
30489         */
30490          
30491         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30492         
30493     },
30494     
30495     _getItemLayoutPosition : function( item )  // what is item?
30496     {
30497         // we resize the item to our columnWidth..
30498       
30499         item.setWidth(this.columnWidth);
30500         item.autoBoxAdjust  = false;
30501         
30502         var sz = item.getSize();
30503  
30504         // how many columns does this brick span
30505         var remainder = this.containerWidth % this.columnWidth;
30506         
30507         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30508         // round if off by 1 pixel, otherwise use ceil
30509         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30510         colSpan = Math.min( colSpan, this.cols );
30511         
30512         // normally this should be '1' as we dont' currently allow multi width columns..
30513         
30514         var colGroup = this._getColGroup( colSpan );
30515         // get the minimum Y value from the columns
30516         var minimumY = Math.min.apply( Math, colGroup );
30517         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30518         
30519         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30520          
30521         // position the brick
30522         var position = {
30523             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30524             y: this.currentSize.y + minimumY + this.padHeight
30525         };
30526         
30527         Roo.log(position);
30528         // apply setHeight to necessary columns
30529         var setHeight = minimumY + sz.height + this.padHeight;
30530         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30531         
30532         var setSpan = this.cols + 1 - colGroup.length;
30533         for ( var i = 0; i < setSpan; i++ ) {
30534           this.colYs[ shortColIndex + i ] = setHeight ;
30535         }
30536       
30537         return position;
30538     },
30539     
30540     /**
30541      * @param {Number} colSpan - number of columns the element spans
30542      * @returns {Array} colGroup
30543      */
30544     _getColGroup : function( colSpan )
30545     {
30546         if ( colSpan < 2 ) {
30547           // if brick spans only one column, use all the column Ys
30548           return this.colYs;
30549         }
30550       
30551         var colGroup = [];
30552         // how many different places could this brick fit horizontally
30553         var groupCount = this.cols + 1 - colSpan;
30554         // for each group potential horizontal position
30555         for ( var i = 0; i < groupCount; i++ ) {
30556           // make an array of colY values for that one group
30557           var groupColYs = this.colYs.slice( i, i + colSpan );
30558           // and get the max value of the array
30559           colGroup[i] = Math.max.apply( Math, groupColYs );
30560         }
30561         return colGroup;
30562     },
30563     /*
30564     _manageStamp : function( stamp )
30565     {
30566         var stampSize =  stamp.getSize();
30567         var offset = stamp.getBox();
30568         // get the columns that this stamp affects
30569         var firstX = this.isOriginLeft ? offset.x : offset.right;
30570         var lastX = firstX + stampSize.width;
30571         var firstCol = Math.floor( firstX / this.columnWidth );
30572         firstCol = Math.max( 0, firstCol );
30573         
30574         var lastCol = Math.floor( lastX / this.columnWidth );
30575         // lastCol should not go over if multiple of columnWidth #425
30576         lastCol -= lastX % this.columnWidth ? 0 : 1;
30577         lastCol = Math.min( this.cols - 1, lastCol );
30578         
30579         // set colYs to bottom of the stamp
30580         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30581             stampSize.height;
30582             
30583         for ( var i = firstCol; i <= lastCol; i++ ) {
30584           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30585         }
30586     },
30587     */
30588     
30589     _getContainerSize : function()
30590     {
30591         this.maxY = Math.max.apply( Math, this.colYs );
30592         var size = {
30593             height: this.maxY
30594         };
30595       
30596         if ( this.isFitWidth ) {
30597             size.width = this._getContainerFitWidth();
30598         }
30599       
30600         return size;
30601     },
30602     
30603     _getContainerFitWidth : function()
30604     {
30605         var unusedCols = 0;
30606         // count unused columns
30607         var i = this.cols;
30608         while ( --i ) {
30609           if ( this.colYs[i] !== 0 ) {
30610             break;
30611           }
30612           unusedCols++;
30613         }
30614         // fit container to columns that have been used
30615         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30616     },
30617     
30618     needsResizeLayout : function()
30619     {
30620         var previousWidth = this.containerWidth;
30621         this.getContainerWidth();
30622         return previousWidth !== this.containerWidth;
30623     }
30624  
30625 });
30626
30627  
30628
30629  /*
30630  * - LGPL
30631  *
30632  * element
30633  * 
30634  */
30635
30636 /**
30637  * @class Roo.bootstrap.MasonryBrick
30638  * @extends Roo.bootstrap.Component
30639  * Bootstrap MasonryBrick class
30640  * 
30641  * @constructor
30642  * Create a new MasonryBrick
30643  * @param {Object} config The config object
30644  */
30645
30646 Roo.bootstrap.MasonryBrick = function(config){
30647     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30648     
30649     this.addEvents({
30650         // raw events
30651         /**
30652          * @event click
30653          * When a MasonryBrick is clcik
30654          * @param {Roo.bootstrap.MasonryBrick} this
30655          * @param {Roo.EventObject} e
30656          */
30657         "click" : true
30658     });
30659 };
30660
30661 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30662     
30663     /**
30664      * @cfg {String} title
30665      */   
30666     title : '',
30667     /**
30668      * @cfg {String} html
30669      */   
30670     html : '',
30671     /**
30672      * @cfg {String} bgimage
30673      */   
30674     bgimage : '',
30675     /**
30676      * @cfg {String} videourl
30677      */   
30678     videourl : '',
30679     /**
30680      * @cfg {String} cls
30681      */   
30682     cls : '',
30683     /**
30684      * @cfg {String} href
30685      */   
30686     href : '',
30687     /**
30688      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30689      */   
30690     size : 'xs',
30691     
30692     /**
30693      * @cfg {String} (center|bottom) placetitle
30694      */   
30695     placetitle : '',
30696     
30697     getAutoCreate : function()
30698     {
30699         var cls = 'masonry-brick';
30700         
30701         if(this.href.length){
30702             cls += ' masonry-brick-link';
30703         }
30704         
30705         if(this.bgimage.length){
30706             cls += ' masonry-brick-image';
30707         }
30708         
30709         if(this.size){
30710             cls += ' masonry-' + this.size + '-brick';
30711         }
30712         
30713         if(this.placetitle.length){
30714             
30715             switch (this.placetitle) {
30716                 case 'center' :
30717                     cls += ' masonry-center-title';
30718                     break;
30719                 case 'bottom' :
30720                     cls += ' masonry-bottom-title';
30721                     break;
30722                 default:
30723                     break;
30724             }
30725             
30726         } else {
30727             if(!this.html.length && !this.bgimage.length){
30728                 cls += ' masonry-center-title';
30729             }
30730
30731             if(!this.html.length && this.bgimage.length){
30732                 cls += ' masonry-bottom-title';
30733             }
30734         }
30735         
30736         if(this.cls){
30737             cls += ' ' + this.cls;
30738         }
30739         
30740         var cfg = {
30741             tag: (this.href.length) ? 'a' : 'div',
30742             cls: cls,
30743             cn: [
30744                 {
30745                     tag: 'div',
30746                     cls: 'masonry-brick-paragraph',
30747                     cn: []
30748                 }
30749             ]
30750         };
30751         
30752         if(this.href.length){
30753             cfg.href = this.href;
30754         }
30755         
30756         var cn = cfg.cn[0].cn;
30757         
30758         if(this.title.length){
30759             cn.push({
30760                 tag: 'h4',
30761                 cls: 'masonry-brick-title',
30762                 html: this.title
30763             });
30764         }
30765         
30766         if(this.html.length){
30767             cn.push({
30768                 tag: 'p',
30769                 cls: 'masonry-brick-text',
30770                 html: this.html
30771             });
30772         }  
30773         if (!this.title.length && !this.html.length) {
30774             cfg.cn[0].cls += ' hide';
30775         }
30776         
30777         if(this.bgimage.length){
30778             cfg.cn.push({
30779                 tag: 'img',
30780                 cls: 'masonry-brick-image-view',
30781                 src: this.bgimage
30782             });
30783         }
30784         if(this.videourl.length){
30785             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30786             // youtube support only?
30787             cfg.cn.push({
30788                 tag: 'iframe',
30789                 cls: 'masonry-brick-image-view',
30790                 src: vurl,
30791                 frameborder : 0,
30792                 allowfullscreen : true
30793             });
30794             
30795             
30796         }
30797         return cfg;
30798         
30799     },
30800     
30801     initEvents: function() 
30802     {
30803         switch (this.size) {
30804             case 'xs' :
30805 //                this.intSize = 1;
30806                 this.x = 1;
30807                 this.y = 1;
30808                 break;
30809             case 'sm' :
30810 //                this.intSize = 2;
30811                 this.x = 2;
30812                 this.y = 2;
30813                 break;
30814             case 'md' :
30815             case 'md-left' :
30816             case 'md-right' :
30817 //                this.intSize = 3;
30818                 this.x = 3;
30819                 this.y = 3;
30820                 break;
30821             case 'tall' :
30822 //                this.intSize = 3;
30823                 this.x = 2;
30824                 this.y = 3;
30825                 break;
30826             case 'wide' :
30827 //                this.intSize = 3;
30828                 this.x = 3;
30829                 this.y = 2;
30830                 break;
30831             case 'wide-thin' :
30832 //                this.intSize = 3;
30833                 this.x = 3;
30834                 this.y = 1;
30835                 break;
30836                         
30837             default :
30838                 break;
30839         }
30840         
30841         
30842         
30843         if(Roo.isTouch){
30844             this.el.on('touchstart', this.onTouchStart, this);
30845             this.el.on('touchmove', this.onTouchMove, this);
30846             this.el.on('touchend', this.onTouchEnd, this);
30847             this.el.on('contextmenu', this.onContextMenu, this);
30848         } else {
30849             this.el.on('mouseenter'  ,this.enter, this);
30850             this.el.on('mouseleave', this.leave, this);
30851         }
30852         
30853         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30854             this.parent().bricks.push(this);   
30855         }
30856         
30857     },
30858     
30859     onClick: function(e, el)
30860     {
30861         if(!Roo.isTouch){
30862             return;
30863         }
30864         
30865         var time = this.endTimer - this.startTimer;
30866         
30867         //alert(time);
30868         
30869         if(time < 1000){
30870             return;
30871         }
30872         
30873         e.preventDefault();
30874     },
30875     
30876     enter: function(e, el)
30877     {
30878         e.preventDefault();
30879         
30880         if(this.bgimage.length && this.html.length){
30881             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30882         }
30883     },
30884     
30885     leave: function(e, el)
30886     {
30887         e.preventDefault();
30888         
30889         if(this.bgimage.length && this.html.length){
30890             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30891         }
30892     },
30893     
30894     onTouchStart: function(e, el)
30895     {
30896 //        e.preventDefault();
30897         
30898         this.touchmoved = false;
30899         
30900         if(!this.bgimage.length || !this.html.length){
30901             return;
30902         }
30903         
30904         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30905         
30906         this.timer = new Date().getTime();
30907         
30908     },
30909     
30910     onTouchMove: function(e, el)
30911     {
30912         this.touchmoved = true;
30913     },
30914     
30915     onContextMenu : function(e,el)
30916     {
30917         e.preventDefault();
30918         e.stopPropagation();
30919         return false;
30920     },
30921     
30922     onTouchEnd: function(e, el)
30923     {
30924 //        e.preventDefault();
30925         
30926         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30927         
30928             this.leave(e,el);
30929             
30930             return;
30931         }
30932         
30933         if(!this.bgimage.length || !this.html.length){
30934             
30935             if(this.href.length){
30936                 window.location.href = this.href;
30937             }
30938             
30939             return;
30940         }
30941         
30942         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30943         
30944         window.location.href = this.href;
30945     }
30946     
30947 });
30948
30949  
30950
30951  /*
30952  * - LGPL
30953  *
30954  * element
30955  * 
30956  */
30957
30958 /**
30959  * @class Roo.bootstrap.Brick
30960  * @extends Roo.bootstrap.Component
30961  * Bootstrap Brick class
30962  * 
30963  * @constructor
30964  * Create a new Brick
30965  * @param {Object} config The config object
30966  */
30967
30968 Roo.bootstrap.Brick = function(config){
30969     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30970     
30971     this.addEvents({
30972         // raw events
30973         /**
30974          * @event click
30975          * When a Brick is click
30976          * @param {Roo.bootstrap.Brick} this
30977          * @param {Roo.EventObject} e
30978          */
30979         "click" : true
30980     });
30981 };
30982
30983 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30984     
30985     /**
30986      * @cfg {String} title
30987      */   
30988     title : '',
30989     /**
30990      * @cfg {String} html
30991      */   
30992     html : '',
30993     /**
30994      * @cfg {String} bgimage
30995      */   
30996     bgimage : '',
30997     /**
30998      * @cfg {String} cls
30999      */   
31000     cls : '',
31001     /**
31002      * @cfg {String} href
31003      */   
31004     href : '',
31005     /**
31006      * @cfg {String} video
31007      */   
31008     video : '',
31009     /**
31010      * @cfg {Boolean} square
31011      */   
31012     square : true,
31013     
31014     getAutoCreate : function()
31015     {
31016         var cls = 'roo-brick';
31017         
31018         if(this.href.length){
31019             cls += ' roo-brick-link';
31020         }
31021         
31022         if(this.bgimage.length){
31023             cls += ' roo-brick-image';
31024         }
31025         
31026         if(!this.html.length && !this.bgimage.length){
31027             cls += ' roo-brick-center-title';
31028         }
31029         
31030         if(!this.html.length && this.bgimage.length){
31031             cls += ' roo-brick-bottom-title';
31032         }
31033         
31034         if(this.cls){
31035             cls += ' ' + this.cls;
31036         }
31037         
31038         var cfg = {
31039             tag: (this.href.length) ? 'a' : 'div',
31040             cls: cls,
31041             cn: [
31042                 {
31043                     tag: 'div',
31044                     cls: 'roo-brick-paragraph',
31045                     cn: []
31046                 }
31047             ]
31048         };
31049         
31050         if(this.href.length){
31051             cfg.href = this.href;
31052         }
31053         
31054         var cn = cfg.cn[0].cn;
31055         
31056         if(this.title.length){
31057             cn.push({
31058                 tag: 'h4',
31059                 cls: 'roo-brick-title',
31060                 html: this.title
31061             });
31062         }
31063         
31064         if(this.html.length){
31065             cn.push({
31066                 tag: 'p',
31067                 cls: 'roo-brick-text',
31068                 html: this.html
31069             });
31070         } else {
31071             cn.cls += ' hide';
31072         }
31073         
31074         if(this.bgimage.length){
31075             cfg.cn.push({
31076                 tag: 'img',
31077                 cls: 'roo-brick-image-view',
31078                 src: this.bgimage
31079             });
31080         }
31081         
31082         return cfg;
31083     },
31084     
31085     initEvents: function() 
31086     {
31087         if(this.title.length || this.html.length){
31088             this.el.on('mouseenter'  ,this.enter, this);
31089             this.el.on('mouseleave', this.leave, this);
31090         }
31091         
31092         
31093         Roo.EventManager.onWindowResize(this.resize, this); 
31094         
31095         this.resize();
31096     },
31097     
31098     resize : function()
31099     {
31100         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31101         
31102         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31103 //        paragraph.setHeight(paragraph.getWidth());
31104         
31105         if(this.bgimage.length){
31106             var image = this.el.select('.roo-brick-image-view', true).first();
31107             image.setWidth(paragraph.getWidth());
31108             image.setHeight(paragraph.getWidth());
31109         }
31110         
31111     },
31112     
31113     enter: function(e, el)
31114     {
31115         e.preventDefault();
31116         
31117         if(this.bgimage.length){
31118             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31119             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31120         }
31121     },
31122     
31123     leave: function(e, el)
31124     {
31125         e.preventDefault();
31126         
31127         if(this.bgimage.length){
31128             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31129             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31130         }
31131     }
31132     
31133 });
31134
31135  
31136
31137  /*
31138  * Based on:
31139  * Ext JS Library 1.1.1
31140  * Copyright(c) 2006-2007, Ext JS, LLC.
31141  *
31142  * Originally Released Under LGPL - original licence link has changed is not relivant.
31143  *
31144  * Fork - LGPL
31145  * <script type="text/javascript">
31146  */
31147
31148
31149 /**
31150  * @class Roo.bootstrap.SplitBar
31151  * @extends Roo.util.Observable
31152  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31153  * <br><br>
31154  * Usage:
31155  * <pre><code>
31156 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31157                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31158 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31159 split.minSize = 100;
31160 split.maxSize = 600;
31161 split.animate = true;
31162 split.on('moved', splitterMoved);
31163 </code></pre>
31164  * @constructor
31165  * Create a new SplitBar
31166  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31167  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31168  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31169  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31170                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31171                         position of the SplitBar).
31172  */
31173 Roo.bootstrap.SplitBar = function(cfg){
31174     
31175     /** @private */
31176     
31177     //{
31178     //  dragElement : elm
31179     //  resizingElement: el,
31180         // optional..
31181     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31182     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31183         // existingProxy ???
31184     //}
31185     
31186     this.el = Roo.get(cfg.dragElement, true);
31187     this.el.dom.unselectable = "on";
31188     /** @private */
31189     this.resizingEl = Roo.get(cfg.resizingElement, true);
31190
31191     /**
31192      * @private
31193      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31194      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31195      * @type Number
31196      */
31197     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31198     
31199     /**
31200      * The minimum size of the resizing element. (Defaults to 0)
31201      * @type Number
31202      */
31203     this.minSize = 0;
31204     
31205     /**
31206      * The maximum size of the resizing element. (Defaults to 2000)
31207      * @type Number
31208      */
31209     this.maxSize = 2000;
31210     
31211     /**
31212      * Whether to animate the transition to the new size
31213      * @type Boolean
31214      */
31215     this.animate = false;
31216     
31217     /**
31218      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31219      * @type Boolean
31220      */
31221     this.useShim = false;
31222     
31223     /** @private */
31224     this.shim = null;
31225     
31226     if(!cfg.existingProxy){
31227         /** @private */
31228         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31229     }else{
31230         this.proxy = Roo.get(cfg.existingProxy).dom;
31231     }
31232     /** @private */
31233     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31234     
31235     /** @private */
31236     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31237     
31238     /** @private */
31239     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31240     
31241     /** @private */
31242     this.dragSpecs = {};
31243     
31244     /**
31245      * @private The adapter to use to positon and resize elements
31246      */
31247     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31248     this.adapter.init(this);
31249     
31250     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31251         /** @private */
31252         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31253         this.el.addClass("roo-splitbar-h");
31254     }else{
31255         /** @private */
31256         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31257         this.el.addClass("roo-splitbar-v");
31258     }
31259     
31260     this.addEvents({
31261         /**
31262          * @event resize
31263          * Fires when the splitter is moved (alias for {@link #event-moved})
31264          * @param {Roo.bootstrap.SplitBar} this
31265          * @param {Number} newSize the new width or height
31266          */
31267         "resize" : true,
31268         /**
31269          * @event moved
31270          * Fires when the splitter is moved
31271          * @param {Roo.bootstrap.SplitBar} this
31272          * @param {Number} newSize the new width or height
31273          */
31274         "moved" : true,
31275         /**
31276          * @event beforeresize
31277          * Fires before the splitter is dragged
31278          * @param {Roo.bootstrap.SplitBar} this
31279          */
31280         "beforeresize" : true,
31281
31282         "beforeapply" : true
31283     });
31284
31285     Roo.util.Observable.call(this);
31286 };
31287
31288 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31289     onStartProxyDrag : function(x, y){
31290         this.fireEvent("beforeresize", this);
31291         if(!this.overlay){
31292             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31293             o.unselectable();
31294             o.enableDisplayMode("block");
31295             // all splitbars share the same overlay
31296             Roo.bootstrap.SplitBar.prototype.overlay = o;
31297         }
31298         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31299         this.overlay.show();
31300         Roo.get(this.proxy).setDisplayed("block");
31301         var size = this.adapter.getElementSize(this);
31302         this.activeMinSize = this.getMinimumSize();;
31303         this.activeMaxSize = this.getMaximumSize();;
31304         var c1 = size - this.activeMinSize;
31305         var c2 = Math.max(this.activeMaxSize - size, 0);
31306         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31307             this.dd.resetConstraints();
31308             this.dd.setXConstraint(
31309                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31310                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31311             );
31312             this.dd.setYConstraint(0, 0);
31313         }else{
31314             this.dd.resetConstraints();
31315             this.dd.setXConstraint(0, 0);
31316             this.dd.setYConstraint(
31317                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31318                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31319             );
31320          }
31321         this.dragSpecs.startSize = size;
31322         this.dragSpecs.startPoint = [x, y];
31323         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31324     },
31325     
31326     /** 
31327      * @private Called after the drag operation by the DDProxy
31328      */
31329     onEndProxyDrag : function(e){
31330         Roo.get(this.proxy).setDisplayed(false);
31331         var endPoint = Roo.lib.Event.getXY(e);
31332         if(this.overlay){
31333             this.overlay.hide();
31334         }
31335         var newSize;
31336         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31337             newSize = this.dragSpecs.startSize + 
31338                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31339                     endPoint[0] - this.dragSpecs.startPoint[0] :
31340                     this.dragSpecs.startPoint[0] - endPoint[0]
31341                 );
31342         }else{
31343             newSize = this.dragSpecs.startSize + 
31344                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31345                     endPoint[1] - this.dragSpecs.startPoint[1] :
31346                     this.dragSpecs.startPoint[1] - endPoint[1]
31347                 );
31348         }
31349         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31350         if(newSize != this.dragSpecs.startSize){
31351             if(this.fireEvent('beforeapply', this, newSize) !== false){
31352                 this.adapter.setElementSize(this, newSize);
31353                 this.fireEvent("moved", this, newSize);
31354                 this.fireEvent("resize", this, newSize);
31355             }
31356         }
31357     },
31358     
31359     /**
31360      * Get the adapter this SplitBar uses
31361      * @return The adapter object
31362      */
31363     getAdapter : function(){
31364         return this.adapter;
31365     },
31366     
31367     /**
31368      * Set the adapter this SplitBar uses
31369      * @param {Object} adapter A SplitBar adapter object
31370      */
31371     setAdapter : function(adapter){
31372         this.adapter = adapter;
31373         this.adapter.init(this);
31374     },
31375     
31376     /**
31377      * Gets the minimum size for the resizing element
31378      * @return {Number} The minimum size
31379      */
31380     getMinimumSize : function(){
31381         return this.minSize;
31382     },
31383     
31384     /**
31385      * Sets the minimum size for the resizing element
31386      * @param {Number} minSize The minimum size
31387      */
31388     setMinimumSize : function(minSize){
31389         this.minSize = minSize;
31390     },
31391     
31392     /**
31393      * Gets the maximum size for the resizing element
31394      * @return {Number} The maximum size
31395      */
31396     getMaximumSize : function(){
31397         return this.maxSize;
31398     },
31399     
31400     /**
31401      * Sets the maximum size for the resizing element
31402      * @param {Number} maxSize The maximum size
31403      */
31404     setMaximumSize : function(maxSize){
31405         this.maxSize = maxSize;
31406     },
31407     
31408     /**
31409      * Sets the initialize size for the resizing element
31410      * @param {Number} size The initial size
31411      */
31412     setCurrentSize : function(size){
31413         var oldAnimate = this.animate;
31414         this.animate = false;
31415         this.adapter.setElementSize(this, size);
31416         this.animate = oldAnimate;
31417     },
31418     
31419     /**
31420      * Destroy this splitbar. 
31421      * @param {Boolean} removeEl True to remove the element
31422      */
31423     destroy : function(removeEl){
31424         if(this.shim){
31425             this.shim.remove();
31426         }
31427         this.dd.unreg();
31428         this.proxy.parentNode.removeChild(this.proxy);
31429         if(removeEl){
31430             this.el.remove();
31431         }
31432     }
31433 });
31434
31435 /**
31436  * @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.
31437  */
31438 Roo.bootstrap.SplitBar.createProxy = function(dir){
31439     var proxy = new Roo.Element(document.createElement("div"));
31440     proxy.unselectable();
31441     var cls = 'roo-splitbar-proxy';
31442     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31443     document.body.appendChild(proxy.dom);
31444     return proxy.dom;
31445 };
31446
31447 /** 
31448  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31449  * Default Adapter. It assumes the splitter and resizing element are not positioned
31450  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31451  */
31452 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31453 };
31454
31455 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31456     // do nothing for now
31457     init : function(s){
31458     
31459     },
31460     /**
31461      * Called before drag operations to get the current size of the resizing element. 
31462      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31463      */
31464      getElementSize : function(s){
31465         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31466             return s.resizingEl.getWidth();
31467         }else{
31468             return s.resizingEl.getHeight();
31469         }
31470     },
31471     
31472     /**
31473      * Called after drag operations to set the size of the resizing element.
31474      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31475      * @param {Number} newSize The new size to set
31476      * @param {Function} onComplete A function to be invoked when resizing is complete
31477      */
31478     setElementSize : function(s, newSize, onComplete){
31479         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31480             if(!s.animate){
31481                 s.resizingEl.setWidth(newSize);
31482                 if(onComplete){
31483                     onComplete(s, newSize);
31484                 }
31485             }else{
31486                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31487             }
31488         }else{
31489             
31490             if(!s.animate){
31491                 s.resizingEl.setHeight(newSize);
31492                 if(onComplete){
31493                     onComplete(s, newSize);
31494                 }
31495             }else{
31496                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31497             }
31498         }
31499     }
31500 };
31501
31502 /** 
31503  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31504  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31505  * Adapter that  moves the splitter element to align with the resized sizing element. 
31506  * Used with an absolute positioned SplitBar.
31507  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31508  * document.body, make sure you assign an id to the body element.
31509  */
31510 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31511     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31512     this.container = Roo.get(container);
31513 };
31514
31515 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31516     init : function(s){
31517         this.basic.init(s);
31518     },
31519     
31520     getElementSize : function(s){
31521         return this.basic.getElementSize(s);
31522     },
31523     
31524     setElementSize : function(s, newSize, onComplete){
31525         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31526     },
31527     
31528     moveSplitter : function(s){
31529         var yes = Roo.bootstrap.SplitBar;
31530         switch(s.placement){
31531             case yes.LEFT:
31532                 s.el.setX(s.resizingEl.getRight());
31533                 break;
31534             case yes.RIGHT:
31535                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31536                 break;
31537             case yes.TOP:
31538                 s.el.setY(s.resizingEl.getBottom());
31539                 break;
31540             case yes.BOTTOM:
31541                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31542                 break;
31543         }
31544     }
31545 };
31546
31547 /**
31548  * Orientation constant - Create a vertical SplitBar
31549  * @static
31550  * @type Number
31551  */
31552 Roo.bootstrap.SplitBar.VERTICAL = 1;
31553
31554 /**
31555  * Orientation constant - Create a horizontal SplitBar
31556  * @static
31557  * @type Number
31558  */
31559 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31560
31561 /**
31562  * Placement constant - The resizing element is to the left of the splitter element
31563  * @static
31564  * @type Number
31565  */
31566 Roo.bootstrap.SplitBar.LEFT = 1;
31567
31568 /**
31569  * Placement constant - The resizing element is to the right of the splitter element
31570  * @static
31571  * @type Number
31572  */
31573 Roo.bootstrap.SplitBar.RIGHT = 2;
31574
31575 /**
31576  * Placement constant - The resizing element is positioned above the splitter element
31577  * @static
31578  * @type Number
31579  */
31580 Roo.bootstrap.SplitBar.TOP = 3;
31581
31582 /**
31583  * Placement constant - The resizing element is positioned under splitter element
31584  * @static
31585  * @type Number
31586  */
31587 Roo.bootstrap.SplitBar.BOTTOM = 4;
31588 Roo.namespace("Roo.bootstrap.layout");/*
31589  * Based on:
31590  * Ext JS Library 1.1.1
31591  * Copyright(c) 2006-2007, Ext JS, LLC.
31592  *
31593  * Originally Released Under LGPL - original licence link has changed is not relivant.
31594  *
31595  * Fork - LGPL
31596  * <script type="text/javascript">
31597  */
31598  
31599 /**
31600  * @class Roo.bootstrap.layout.Manager
31601  * @extends Roo.bootstrap.Component
31602  * Base class for layout managers.
31603  */
31604 Roo.bootstrap.layout.Manager = function(config)
31605 {
31606     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31607     
31608     
31609      
31610     
31611     
31612     /** false to disable window resize monitoring @type Boolean */
31613     this.monitorWindowResize = true;
31614     this.regions = {};
31615     this.addEvents({
31616         /**
31617          * @event layout
31618          * Fires when a layout is performed. 
31619          * @param {Roo.LayoutManager} this
31620          */
31621         "layout" : true,
31622         /**
31623          * @event regionresized
31624          * Fires when the user resizes a region. 
31625          * @param {Roo.LayoutRegion} region The resized region
31626          * @param {Number} newSize The new size (width for east/west, height for north/south)
31627          */
31628         "regionresized" : true,
31629         /**
31630          * @event regioncollapsed
31631          * Fires when a region is collapsed. 
31632          * @param {Roo.LayoutRegion} region The collapsed region
31633          */
31634         "regioncollapsed" : true,
31635         /**
31636          * @event regionexpanded
31637          * Fires when a region is expanded.  
31638          * @param {Roo.LayoutRegion} region The expanded region
31639          */
31640         "regionexpanded" : true
31641     });
31642     this.updating = false;
31643     
31644     if (config.el) {
31645         this.el = Roo.get(config.el);
31646         this.initEvents();
31647     }
31648     
31649 };
31650
31651 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31652     
31653     
31654     regions : null,
31655     
31656     monitorWindowResize : true,
31657     
31658     
31659     updating : false,
31660     
31661     
31662     onRender : function(ct, position)
31663     {
31664         if(!this.el){
31665             this.el = Roo.get(ct);
31666             this.initEvents();
31667         }
31668     },
31669     
31670     
31671     initEvents: function()
31672     {
31673         
31674         
31675         // ie scrollbar fix
31676         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31677             document.body.scroll = "no";
31678         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31679             this.el.position('relative');
31680         }
31681         this.id = this.el.id;
31682         this.el.addClass("roo-layout-container");
31683         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31684         if(this.el.dom != document.body ) {
31685             this.el.on('resize', this.layout,this);
31686             this.el.on('show', this.layout,this);
31687         }
31688
31689     },
31690     
31691     /**
31692      * Returns true if this layout is currently being updated
31693      * @return {Boolean}
31694      */
31695     isUpdating : function(){
31696         return this.updating; 
31697     },
31698     
31699     /**
31700      * Suspend the LayoutManager from doing auto-layouts while
31701      * making multiple add or remove calls
31702      */
31703     beginUpdate : function(){
31704         this.updating = true;    
31705     },
31706     
31707     /**
31708      * Restore auto-layouts and optionally disable the manager from performing a layout
31709      * @param {Boolean} noLayout true to disable a layout update 
31710      */
31711     endUpdate : function(noLayout){
31712         this.updating = false;
31713         if(!noLayout){
31714             this.layout();
31715         }    
31716     },
31717     
31718     layout: function(){
31719         // abstract...
31720     },
31721     
31722     onRegionResized : function(region, newSize){
31723         this.fireEvent("regionresized", region, newSize);
31724         this.layout();
31725     },
31726     
31727     onRegionCollapsed : function(region){
31728         this.fireEvent("regioncollapsed", region);
31729     },
31730     
31731     onRegionExpanded : function(region){
31732         this.fireEvent("regionexpanded", region);
31733     },
31734         
31735     /**
31736      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31737      * performs box-model adjustments.
31738      * @return {Object} The size as an object {width: (the width), height: (the height)}
31739      */
31740     getViewSize : function()
31741     {
31742         var size;
31743         if(this.el.dom != document.body){
31744             size = this.el.getSize();
31745         }else{
31746             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31747         }
31748         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31749         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31750         return size;
31751     },
31752     
31753     /**
31754      * Returns the Element this layout is bound to.
31755      * @return {Roo.Element}
31756      */
31757     getEl : function(){
31758         return this.el;
31759     },
31760     
31761     /**
31762      * Returns the specified region.
31763      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31764      * @return {Roo.LayoutRegion}
31765      */
31766     getRegion : function(target){
31767         return this.regions[target.toLowerCase()];
31768     },
31769     
31770     onWindowResize : function(){
31771         if(this.monitorWindowResize){
31772             this.layout();
31773         }
31774     }
31775 });/*
31776  * Based on:
31777  * Ext JS Library 1.1.1
31778  * Copyright(c) 2006-2007, Ext JS, LLC.
31779  *
31780  * Originally Released Under LGPL - original licence link has changed is not relivant.
31781  *
31782  * Fork - LGPL
31783  * <script type="text/javascript">
31784  */
31785 /**
31786  * @class Roo.bootstrap.layout.Border
31787  * @extends Roo.bootstrap.layout.Manager
31788  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31789  * please see: examples/bootstrap/nested.html<br><br>
31790  
31791 <b>The container the layout is rendered into can be either the body element or any other element.
31792 If it is not the body element, the container needs to either be an absolute positioned element,
31793 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31794 the container size if it is not the body element.</b>
31795
31796 * @constructor
31797 * Create a new Border
31798 * @param {Object} config Configuration options
31799  */
31800 Roo.bootstrap.layout.Border = function(config){
31801     config = config || {};
31802     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31803     
31804     
31805     
31806     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31807         if(config[region]){
31808             config[region].region = region;
31809             this.addRegion(config[region]);
31810         }
31811     },this);
31812     
31813 };
31814
31815 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31816
31817 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31818     /**
31819      * Creates and adds a new region if it doesn't already exist.
31820      * @param {String} target The target region key (north, south, east, west or center).
31821      * @param {Object} config The regions config object
31822      * @return {BorderLayoutRegion} The new region
31823      */
31824     addRegion : function(config)
31825     {
31826         if(!this.regions[config.region]){
31827             var r = this.factory(config);
31828             this.bindRegion(r);
31829         }
31830         return this.regions[config.region];
31831     },
31832
31833     // private (kinda)
31834     bindRegion : function(r){
31835         this.regions[r.config.region] = r;
31836         
31837         r.on("visibilitychange",    this.layout, this);
31838         r.on("paneladded",          this.layout, this);
31839         r.on("panelremoved",        this.layout, this);
31840         r.on("invalidated",         this.layout, this);
31841         r.on("resized",             this.onRegionResized, this);
31842         r.on("collapsed",           this.onRegionCollapsed, this);
31843         r.on("expanded",            this.onRegionExpanded, this);
31844     },
31845
31846     /**
31847      * Performs a layout update.
31848      */
31849     layout : function()
31850     {
31851         if(this.updating) {
31852             return;
31853         }
31854         
31855         // render all the rebions if they have not been done alreayd?
31856         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31857             if(this.regions[region] && !this.regions[region].bodyEl){
31858                 this.regions[region].onRender(this.el)
31859             }
31860         },this);
31861         
31862         var size = this.getViewSize();
31863         var w = size.width;
31864         var h = size.height;
31865         var centerW = w;
31866         var centerH = h;
31867         var centerY = 0;
31868         var centerX = 0;
31869         //var x = 0, y = 0;
31870
31871         var rs = this.regions;
31872         var north = rs["north"];
31873         var south = rs["south"]; 
31874         var west = rs["west"];
31875         var east = rs["east"];
31876         var center = rs["center"];
31877         //if(this.hideOnLayout){ // not supported anymore
31878             //c.el.setStyle("display", "none");
31879         //}
31880         if(north && north.isVisible()){
31881             var b = north.getBox();
31882             var m = north.getMargins();
31883             b.width = w - (m.left+m.right);
31884             b.x = m.left;
31885             b.y = m.top;
31886             centerY = b.height + b.y + m.bottom;
31887             centerH -= centerY;
31888             north.updateBox(this.safeBox(b));
31889         }
31890         if(south && south.isVisible()){
31891             var b = south.getBox();
31892             var m = south.getMargins();
31893             b.width = w - (m.left+m.right);
31894             b.x = m.left;
31895             var totalHeight = (b.height + m.top + m.bottom);
31896             b.y = h - totalHeight + m.top;
31897             centerH -= totalHeight;
31898             south.updateBox(this.safeBox(b));
31899         }
31900         if(west && west.isVisible()){
31901             var b = west.getBox();
31902             var m = west.getMargins();
31903             b.height = centerH - (m.top+m.bottom);
31904             b.x = m.left;
31905             b.y = centerY + m.top;
31906             var totalWidth = (b.width + m.left + m.right);
31907             centerX += totalWidth;
31908             centerW -= totalWidth;
31909             west.updateBox(this.safeBox(b));
31910         }
31911         if(east && east.isVisible()){
31912             var b = east.getBox();
31913             var m = east.getMargins();
31914             b.height = centerH - (m.top+m.bottom);
31915             var totalWidth = (b.width + m.left + m.right);
31916             b.x = w - totalWidth + m.left;
31917             b.y = centerY + m.top;
31918             centerW -= totalWidth;
31919             east.updateBox(this.safeBox(b));
31920         }
31921         if(center){
31922             var m = center.getMargins();
31923             var centerBox = {
31924                 x: centerX + m.left,
31925                 y: centerY + m.top,
31926                 width: centerW - (m.left+m.right),
31927                 height: centerH - (m.top+m.bottom)
31928             };
31929             //if(this.hideOnLayout){
31930                 //center.el.setStyle("display", "block");
31931             //}
31932             center.updateBox(this.safeBox(centerBox));
31933         }
31934         this.el.repaint();
31935         this.fireEvent("layout", this);
31936     },
31937
31938     // private
31939     safeBox : function(box){
31940         box.width = Math.max(0, box.width);
31941         box.height = Math.max(0, box.height);
31942         return box;
31943     },
31944
31945     /**
31946      * Adds a ContentPanel (or subclass) to this layout.
31947      * @param {String} target The target region key (north, south, east, west or center).
31948      * @param {Roo.ContentPanel} panel The panel to add
31949      * @return {Roo.ContentPanel} The added panel
31950      */
31951     add : function(target, panel){
31952          
31953         target = target.toLowerCase();
31954         return this.regions[target].add(panel);
31955     },
31956
31957     /**
31958      * Remove a ContentPanel (or subclass) to this layout.
31959      * @param {String} target The target region key (north, south, east, west or center).
31960      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31961      * @return {Roo.ContentPanel} The removed panel
31962      */
31963     remove : function(target, panel){
31964         target = target.toLowerCase();
31965         return this.regions[target].remove(panel);
31966     },
31967
31968     /**
31969      * Searches all regions for a panel with the specified id
31970      * @param {String} panelId
31971      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31972      */
31973     findPanel : function(panelId){
31974         var rs = this.regions;
31975         for(var target in rs){
31976             if(typeof rs[target] != "function"){
31977                 var p = rs[target].getPanel(panelId);
31978                 if(p){
31979                     return p;
31980                 }
31981             }
31982         }
31983         return null;
31984     },
31985
31986     /**
31987      * Searches all regions for a panel with the specified id and activates (shows) it.
31988      * @param {String/ContentPanel} panelId The panels id or the panel itself
31989      * @return {Roo.ContentPanel} The shown panel or null
31990      */
31991     showPanel : function(panelId) {
31992       var rs = this.regions;
31993       for(var target in rs){
31994          var r = rs[target];
31995          if(typeof r != "function"){
31996             if(r.hasPanel(panelId)){
31997                return r.showPanel(panelId);
31998             }
31999          }
32000       }
32001       return null;
32002    },
32003
32004    /**
32005      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32006      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32007      */
32008    /*
32009     restoreState : function(provider){
32010         if(!provider){
32011             provider = Roo.state.Manager;
32012         }
32013         var sm = new Roo.LayoutStateManager();
32014         sm.init(this, provider);
32015     },
32016 */
32017  
32018  
32019     /**
32020      * Adds a xtype elements to the layout.
32021      * <pre><code>
32022
32023 layout.addxtype({
32024        xtype : 'ContentPanel',
32025        region: 'west',
32026        items: [ .... ]
32027    }
32028 );
32029
32030 layout.addxtype({
32031         xtype : 'NestedLayoutPanel',
32032         region: 'west',
32033         layout: {
32034            center: { },
32035            west: { }   
32036         },
32037         items : [ ... list of content panels or nested layout panels.. ]
32038    }
32039 );
32040 </code></pre>
32041      * @param {Object} cfg Xtype definition of item to add.
32042      */
32043     addxtype : function(cfg)
32044     {
32045         // basically accepts a pannel...
32046         // can accept a layout region..!?!?
32047         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32048         
32049         
32050         // theory?  children can only be panels??
32051         
32052         //if (!cfg.xtype.match(/Panel$/)) {
32053         //    return false;
32054         //}
32055         var ret = false;
32056         
32057         if (typeof(cfg.region) == 'undefined') {
32058             Roo.log("Failed to add Panel, region was not set");
32059             Roo.log(cfg);
32060             return false;
32061         }
32062         var region = cfg.region;
32063         delete cfg.region;
32064         
32065           
32066         var xitems = [];
32067         if (cfg.items) {
32068             xitems = cfg.items;
32069             delete cfg.items;
32070         }
32071         var nb = false;
32072         
32073         switch(cfg.xtype) 
32074         {
32075             case 'Content':  // ContentPanel (el, cfg)
32076             case 'Scroll':  // ContentPanel (el, cfg)
32077             case 'View': 
32078                 cfg.autoCreate = true;
32079                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32080                 //} else {
32081                 //    var el = this.el.createChild();
32082                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32083                 //}
32084                 
32085                 this.add(region, ret);
32086                 break;
32087             
32088             /*
32089             case 'TreePanel': // our new panel!
32090                 cfg.el = this.el.createChild();
32091                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32092                 this.add(region, ret);
32093                 break;
32094             */
32095             
32096             case 'Nest': 
32097                 // create a new Layout (which is  a Border Layout...
32098                 
32099                 var clayout = cfg.layout;
32100                 clayout.el  = this.el.createChild();
32101                 clayout.items   = clayout.items  || [];
32102                 
32103                 delete cfg.layout;
32104                 
32105                 // replace this exitems with the clayout ones..
32106                 xitems = clayout.items;
32107                  
32108                 // force background off if it's in center...
32109                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32110                     cfg.background = false;
32111                 }
32112                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32113                 
32114                 
32115                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32116                 //console.log('adding nested layout panel '  + cfg.toSource());
32117                 this.add(region, ret);
32118                 nb = {}; /// find first...
32119                 break;
32120             
32121             case 'Grid':
32122                 
32123                 // needs grid and region
32124                 
32125                 //var el = this.getRegion(region).el.createChild();
32126                 /*
32127                  *var el = this.el.createChild();
32128                 // create the grid first...
32129                 cfg.grid.container = el;
32130                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32131                 */
32132                 
32133                 if (region == 'center' && this.active ) {
32134                     cfg.background = false;
32135                 }
32136                 
32137                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32138                 
32139                 this.add(region, ret);
32140                 /*
32141                 if (cfg.background) {
32142                     // render grid on panel activation (if panel background)
32143                     ret.on('activate', function(gp) {
32144                         if (!gp.grid.rendered) {
32145                     //        gp.grid.render(el);
32146                         }
32147                     });
32148                 } else {
32149                   //  cfg.grid.render(el);
32150                 }
32151                 */
32152                 break;
32153            
32154            
32155             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32156                 // it was the old xcomponent building that caused this before.
32157                 // espeically if border is the top element in the tree.
32158                 ret = this;
32159                 break; 
32160                 
32161                     
32162                 
32163                 
32164                 
32165             default:
32166                 /*
32167                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32168                     
32169                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32170                     this.add(region, ret);
32171                 } else {
32172                 */
32173                     Roo.log(cfg);
32174                     throw "Can not add '" + cfg.xtype + "' to Border";
32175                     return null;
32176              
32177                                 
32178              
32179         }
32180         this.beginUpdate();
32181         // add children..
32182         var region = '';
32183         var abn = {};
32184         Roo.each(xitems, function(i)  {
32185             region = nb && i.region ? i.region : false;
32186             
32187             var add = ret.addxtype(i);
32188            
32189             if (region) {
32190                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32191                 if (!i.background) {
32192                     abn[region] = nb[region] ;
32193                 }
32194             }
32195             
32196         });
32197         this.endUpdate();
32198
32199         // make the last non-background panel active..
32200         //if (nb) { Roo.log(abn); }
32201         if (nb) {
32202             
32203             for(var r in abn) {
32204                 region = this.getRegion(r);
32205                 if (region) {
32206                     // tried using nb[r], but it does not work..
32207                      
32208                     region.showPanel(abn[r]);
32209                    
32210                 }
32211             }
32212         }
32213         return ret;
32214         
32215     },
32216     
32217     
32218 // private
32219     factory : function(cfg)
32220     {
32221         
32222         var validRegions = Roo.bootstrap.layout.Border.regions;
32223
32224         var target = cfg.region;
32225         cfg.mgr = this;
32226         
32227         var r = Roo.bootstrap.layout;
32228         Roo.log(target);
32229         switch(target){
32230             case "north":
32231                 return new r.North(cfg);
32232             case "south":
32233                 return new r.South(cfg);
32234             case "east":
32235                 return new r.East(cfg);
32236             case "west":
32237                 return new r.West(cfg);
32238             case "center":
32239                 return new r.Center(cfg);
32240         }
32241         throw 'Layout region "'+target+'" not supported.';
32242     }
32243     
32244     
32245 });
32246  /*
32247  * Based on:
32248  * Ext JS Library 1.1.1
32249  * Copyright(c) 2006-2007, Ext JS, LLC.
32250  *
32251  * Originally Released Under LGPL - original licence link has changed is not relivant.
32252  *
32253  * Fork - LGPL
32254  * <script type="text/javascript">
32255  */
32256  
32257 /**
32258  * @class Roo.bootstrap.layout.Basic
32259  * @extends Roo.util.Observable
32260  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32261  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32262  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32263  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32264  * @cfg {string}   region  the region that it inhabits..
32265  * @cfg {bool}   skipConfig skip config?
32266  * 
32267
32268  */
32269 Roo.bootstrap.layout.Basic = function(config){
32270     
32271     this.mgr = config.mgr;
32272     
32273     this.position = config.region;
32274     
32275     var skipConfig = config.skipConfig;
32276     
32277     this.events = {
32278         /**
32279          * @scope Roo.BasicLayoutRegion
32280          */
32281         
32282         /**
32283          * @event beforeremove
32284          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32285          * @param {Roo.LayoutRegion} this
32286          * @param {Roo.ContentPanel} panel The panel
32287          * @param {Object} e The cancel event object
32288          */
32289         "beforeremove" : true,
32290         /**
32291          * @event invalidated
32292          * Fires when the layout for this region is changed.
32293          * @param {Roo.LayoutRegion} this
32294          */
32295         "invalidated" : true,
32296         /**
32297          * @event visibilitychange
32298          * Fires when this region is shown or hidden 
32299          * @param {Roo.LayoutRegion} this
32300          * @param {Boolean} visibility true or false
32301          */
32302         "visibilitychange" : true,
32303         /**
32304          * @event paneladded
32305          * Fires when a panel is added. 
32306          * @param {Roo.LayoutRegion} this
32307          * @param {Roo.ContentPanel} panel The panel
32308          */
32309         "paneladded" : true,
32310         /**
32311          * @event panelremoved
32312          * Fires when a panel is removed. 
32313          * @param {Roo.LayoutRegion} this
32314          * @param {Roo.ContentPanel} panel The panel
32315          */
32316         "panelremoved" : true,
32317         /**
32318          * @event beforecollapse
32319          * Fires when this region before collapse.
32320          * @param {Roo.LayoutRegion} this
32321          */
32322         "beforecollapse" : true,
32323         /**
32324          * @event collapsed
32325          * Fires when this region is collapsed.
32326          * @param {Roo.LayoutRegion} this
32327          */
32328         "collapsed" : true,
32329         /**
32330          * @event expanded
32331          * Fires when this region is expanded.
32332          * @param {Roo.LayoutRegion} this
32333          */
32334         "expanded" : true,
32335         /**
32336          * @event slideshow
32337          * Fires when this region is slid into view.
32338          * @param {Roo.LayoutRegion} this
32339          */
32340         "slideshow" : true,
32341         /**
32342          * @event slidehide
32343          * Fires when this region slides out of view. 
32344          * @param {Roo.LayoutRegion} this
32345          */
32346         "slidehide" : true,
32347         /**
32348          * @event panelactivated
32349          * Fires when a panel is activated. 
32350          * @param {Roo.LayoutRegion} this
32351          * @param {Roo.ContentPanel} panel The activated panel
32352          */
32353         "panelactivated" : true,
32354         /**
32355          * @event resized
32356          * Fires when the user resizes this region. 
32357          * @param {Roo.LayoutRegion} this
32358          * @param {Number} newSize The new size (width for east/west, height for north/south)
32359          */
32360         "resized" : true
32361     };
32362     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32363     this.panels = new Roo.util.MixedCollection();
32364     this.panels.getKey = this.getPanelId.createDelegate(this);
32365     this.box = null;
32366     this.activePanel = null;
32367     // ensure listeners are added...
32368     
32369     if (config.listeners || config.events) {
32370         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32371             listeners : config.listeners || {},
32372             events : config.events || {}
32373         });
32374     }
32375     
32376     if(skipConfig !== true){
32377         this.applyConfig(config);
32378     }
32379 };
32380
32381 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32382 {
32383     getPanelId : function(p){
32384         return p.getId();
32385     },
32386     
32387     applyConfig : function(config){
32388         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32389         this.config = config;
32390         
32391     },
32392     
32393     /**
32394      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32395      * the width, for horizontal (north, south) the height.
32396      * @param {Number} newSize The new width or height
32397      */
32398     resizeTo : function(newSize){
32399         var el = this.el ? this.el :
32400                  (this.activePanel ? this.activePanel.getEl() : null);
32401         if(el){
32402             switch(this.position){
32403                 case "east":
32404                 case "west":
32405                     el.setWidth(newSize);
32406                     this.fireEvent("resized", this, newSize);
32407                 break;
32408                 case "north":
32409                 case "south":
32410                     el.setHeight(newSize);
32411                     this.fireEvent("resized", this, newSize);
32412                 break;                
32413             }
32414         }
32415     },
32416     
32417     getBox : function(){
32418         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32419     },
32420     
32421     getMargins : function(){
32422         return this.margins;
32423     },
32424     
32425     updateBox : function(box){
32426         this.box = box;
32427         var el = this.activePanel.getEl();
32428         el.dom.style.left = box.x + "px";
32429         el.dom.style.top = box.y + "px";
32430         this.activePanel.setSize(box.width, box.height);
32431     },
32432     
32433     /**
32434      * Returns the container element for this region.
32435      * @return {Roo.Element}
32436      */
32437     getEl : function(){
32438         return this.activePanel;
32439     },
32440     
32441     /**
32442      * Returns true if this region is currently visible.
32443      * @return {Boolean}
32444      */
32445     isVisible : function(){
32446         return this.activePanel ? true : false;
32447     },
32448     
32449     setActivePanel : function(panel){
32450         panel = this.getPanel(panel);
32451         if(this.activePanel && this.activePanel != panel){
32452             this.activePanel.setActiveState(false);
32453             this.activePanel.getEl().setLeftTop(-10000,-10000);
32454         }
32455         this.activePanel = panel;
32456         panel.setActiveState(true);
32457         if(this.box){
32458             panel.setSize(this.box.width, this.box.height);
32459         }
32460         this.fireEvent("panelactivated", this, panel);
32461         this.fireEvent("invalidated");
32462     },
32463     
32464     /**
32465      * Show the specified panel.
32466      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32467      * @return {Roo.ContentPanel} The shown panel or null
32468      */
32469     showPanel : function(panel){
32470         panel = this.getPanel(panel);
32471         if(panel){
32472             this.setActivePanel(panel);
32473         }
32474         return panel;
32475     },
32476     
32477     /**
32478      * Get the active panel for this region.
32479      * @return {Roo.ContentPanel} The active panel or null
32480      */
32481     getActivePanel : function(){
32482         return this.activePanel;
32483     },
32484     
32485     /**
32486      * Add the passed ContentPanel(s)
32487      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32488      * @return {Roo.ContentPanel} The panel added (if only one was added)
32489      */
32490     add : function(panel){
32491         if(arguments.length > 1){
32492             for(var i = 0, len = arguments.length; i < len; i++) {
32493                 this.add(arguments[i]);
32494             }
32495             return null;
32496         }
32497         if(this.hasPanel(panel)){
32498             this.showPanel(panel);
32499             return panel;
32500         }
32501         var el = panel.getEl();
32502         if(el.dom.parentNode != this.mgr.el.dom){
32503             this.mgr.el.dom.appendChild(el.dom);
32504         }
32505         if(panel.setRegion){
32506             panel.setRegion(this);
32507         }
32508         this.panels.add(panel);
32509         el.setStyle("position", "absolute");
32510         if(!panel.background){
32511             this.setActivePanel(panel);
32512             if(this.config.initialSize && this.panels.getCount()==1){
32513                 this.resizeTo(this.config.initialSize);
32514             }
32515         }
32516         this.fireEvent("paneladded", this, panel);
32517         return panel;
32518     },
32519     
32520     /**
32521      * Returns true if the panel is in this region.
32522      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32523      * @return {Boolean}
32524      */
32525     hasPanel : function(panel){
32526         if(typeof panel == "object"){ // must be panel obj
32527             panel = panel.getId();
32528         }
32529         return this.getPanel(panel) ? true : false;
32530     },
32531     
32532     /**
32533      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32534      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32535      * @param {Boolean} preservePanel Overrides the config preservePanel option
32536      * @return {Roo.ContentPanel} The panel that was removed
32537      */
32538     remove : function(panel, preservePanel){
32539         panel = this.getPanel(panel);
32540         if(!panel){
32541             return null;
32542         }
32543         var e = {};
32544         this.fireEvent("beforeremove", this, panel, e);
32545         if(e.cancel === true){
32546             return null;
32547         }
32548         var panelId = panel.getId();
32549         this.panels.removeKey(panelId);
32550         return panel;
32551     },
32552     
32553     /**
32554      * Returns the panel specified or null if it's not in this region.
32555      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32556      * @return {Roo.ContentPanel}
32557      */
32558     getPanel : function(id){
32559         if(typeof id == "object"){ // must be panel obj
32560             return id;
32561         }
32562         return this.panels.get(id);
32563     },
32564     
32565     /**
32566      * Returns this regions position (north/south/east/west/center).
32567      * @return {String} 
32568      */
32569     getPosition: function(){
32570         return this.position;    
32571     }
32572 });/*
32573  * Based on:
32574  * Ext JS Library 1.1.1
32575  * Copyright(c) 2006-2007, Ext JS, LLC.
32576  *
32577  * Originally Released Under LGPL - original licence link has changed is not relivant.
32578  *
32579  * Fork - LGPL
32580  * <script type="text/javascript">
32581  */
32582  
32583 /**
32584  * @class Roo.bootstrap.layout.Region
32585  * @extends Roo.bootstrap.layout.Basic
32586  * This class represents a region in a layout manager.
32587  
32588  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32589  * @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})
32590  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32591  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32592  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32593  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32594  * @cfg {String}    title           The title for the region (overrides panel titles)
32595  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32596  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32597  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32598  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32599  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32600  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32601  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32602  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32603  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32604  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32605
32606  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32607  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32608  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32609  * @cfg {Number}    width           For East/West panels
32610  * @cfg {Number}    height          For North/South panels
32611  * @cfg {Boolean}   split           To show the splitter
32612  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32613  * 
32614  * @cfg {string}   cls             Extra CSS classes to add to region
32615  * 
32616  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32617  * @cfg {string}   region  the region that it inhabits..
32618  *
32619
32620  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32621  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32622
32623  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32624  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32625  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32626  */
32627 Roo.bootstrap.layout.Region = function(config)
32628 {
32629     this.applyConfig(config);
32630
32631     var mgr = config.mgr;
32632     var pos = config.region;
32633     config.skipConfig = true;
32634     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32635     
32636     if (mgr.el) {
32637         this.onRender(mgr.el);   
32638     }
32639      
32640     this.visible = true;
32641     this.collapsed = false;
32642     this.unrendered_panels = [];
32643 };
32644
32645 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32646
32647     position: '', // set by wrapper (eg. north/south etc..)
32648     unrendered_panels : null,  // unrendered panels.
32649     createBody : function(){
32650         /** This region's body element 
32651         * @type Roo.Element */
32652         this.bodyEl = this.el.createChild({
32653                 tag: "div",
32654                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32655         });
32656     },
32657
32658     onRender: function(ctr, pos)
32659     {
32660         var dh = Roo.DomHelper;
32661         /** This region's container element 
32662         * @type Roo.Element */
32663         this.el = dh.append(ctr.dom, {
32664                 tag: "div",
32665                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32666             }, true);
32667         /** This region's title element 
32668         * @type Roo.Element */
32669     
32670         this.titleEl = dh.append(this.el.dom,
32671             {
32672                     tag: "div",
32673                     unselectable: "on",
32674                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32675                     children:[
32676                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32677                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32678                     ]}, true);
32679         
32680         this.titleEl.enableDisplayMode();
32681         /** This region's title text element 
32682         * @type HTMLElement */
32683         this.titleTextEl = this.titleEl.dom.firstChild;
32684         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32685         /*
32686         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32687         this.closeBtn.enableDisplayMode();
32688         this.closeBtn.on("click", this.closeClicked, this);
32689         this.closeBtn.hide();
32690     */
32691         this.createBody(this.config);
32692         if(this.config.hideWhenEmpty){
32693             this.hide();
32694             this.on("paneladded", this.validateVisibility, this);
32695             this.on("panelremoved", this.validateVisibility, this);
32696         }
32697         if(this.autoScroll){
32698             this.bodyEl.setStyle("overflow", "auto");
32699         }else{
32700             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32701         }
32702         //if(c.titlebar !== false){
32703             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32704                 this.titleEl.hide();
32705             }else{
32706                 this.titleEl.show();
32707                 if(this.config.title){
32708                     this.titleTextEl.innerHTML = this.config.title;
32709                 }
32710             }
32711         //}
32712         if(this.config.collapsed){
32713             this.collapse(true);
32714         }
32715         if(this.config.hidden){
32716             this.hide();
32717         }
32718         
32719         if (this.unrendered_panels && this.unrendered_panels.length) {
32720             for (var i =0;i< this.unrendered_panels.length; i++) {
32721                 this.add(this.unrendered_panels[i]);
32722             }
32723             this.unrendered_panels = null;
32724             
32725         }
32726         
32727     },
32728     
32729     applyConfig : function(c)
32730     {
32731         /*
32732          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32733             var dh = Roo.DomHelper;
32734             if(c.titlebar !== false){
32735                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32736                 this.collapseBtn.on("click", this.collapse, this);
32737                 this.collapseBtn.enableDisplayMode();
32738                 /*
32739                 if(c.showPin === true || this.showPin){
32740                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32741                     this.stickBtn.enableDisplayMode();
32742                     this.stickBtn.on("click", this.expand, this);
32743                     this.stickBtn.hide();
32744                 }
32745                 
32746             }
32747             */
32748             /** This region's collapsed element
32749             * @type Roo.Element */
32750             /*
32751              *
32752             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32753                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32754             ]}, true);
32755             
32756             if(c.floatable !== false){
32757                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32758                this.collapsedEl.on("click", this.collapseClick, this);
32759             }
32760
32761             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32762                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32763                    id: "message", unselectable: "on", style:{"float":"left"}});
32764                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32765              }
32766             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32767             this.expandBtn.on("click", this.expand, this);
32768             
32769         }
32770         
32771         if(this.collapseBtn){
32772             this.collapseBtn.setVisible(c.collapsible == true);
32773         }
32774         
32775         this.cmargins = c.cmargins || this.cmargins ||
32776                          (this.position == "west" || this.position == "east" ?
32777                              {top: 0, left: 2, right:2, bottom: 0} :
32778                              {top: 2, left: 0, right:0, bottom: 2});
32779         */
32780         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32781         
32782         
32783         this.bottomTabs = c.tabPosition != "top";
32784         
32785         this.autoScroll = c.autoScroll || false;
32786         
32787         
32788        
32789         
32790         this.duration = c.duration || .30;
32791         this.slideDuration = c.slideDuration || .45;
32792         this.config = c;
32793        
32794     },
32795     /**
32796      * Returns true if this region is currently visible.
32797      * @return {Boolean}
32798      */
32799     isVisible : function(){
32800         return this.visible;
32801     },
32802
32803     /**
32804      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32805      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32806      */
32807     //setCollapsedTitle : function(title){
32808     //    title = title || "&#160;";
32809      //   if(this.collapsedTitleTextEl){
32810       //      this.collapsedTitleTextEl.innerHTML = title;
32811        // }
32812     //},
32813
32814     getBox : function(){
32815         var b;
32816       //  if(!this.collapsed){
32817             b = this.el.getBox(false, true);
32818        // }else{
32819           //  b = this.collapsedEl.getBox(false, true);
32820         //}
32821         return b;
32822     },
32823
32824     getMargins : function(){
32825         return this.margins;
32826         //return this.collapsed ? this.cmargins : this.margins;
32827     },
32828 /*
32829     highlight : function(){
32830         this.el.addClass("x-layout-panel-dragover");
32831     },
32832
32833     unhighlight : function(){
32834         this.el.removeClass("x-layout-panel-dragover");
32835     },
32836 */
32837     updateBox : function(box)
32838     {
32839         if (!this.bodyEl) {
32840             return; // not rendered yet..
32841         }
32842         
32843         this.box = box;
32844         if(!this.collapsed){
32845             this.el.dom.style.left = box.x + "px";
32846             this.el.dom.style.top = box.y + "px";
32847             this.updateBody(box.width, box.height);
32848         }else{
32849             this.collapsedEl.dom.style.left = box.x + "px";
32850             this.collapsedEl.dom.style.top = box.y + "px";
32851             this.collapsedEl.setSize(box.width, box.height);
32852         }
32853         if(this.tabs){
32854             this.tabs.autoSizeTabs();
32855         }
32856     },
32857
32858     updateBody : function(w, h)
32859     {
32860         if(w !== null){
32861             this.el.setWidth(w);
32862             w -= this.el.getBorderWidth("rl");
32863             if(this.config.adjustments){
32864                 w += this.config.adjustments[0];
32865             }
32866         }
32867         if(h !== null){
32868             this.el.setHeight(h);
32869             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32870             h -= this.el.getBorderWidth("tb");
32871             if(this.config.adjustments){
32872                 h += this.config.adjustments[1];
32873             }
32874             this.bodyEl.setHeight(h);
32875             if(this.tabs){
32876                 h = this.tabs.syncHeight(h);
32877             }
32878         }
32879         if(this.panelSize){
32880             w = w !== null ? w : this.panelSize.width;
32881             h = h !== null ? h : this.panelSize.height;
32882         }
32883         if(this.activePanel){
32884             var el = this.activePanel.getEl();
32885             w = w !== null ? w : el.getWidth();
32886             h = h !== null ? h : el.getHeight();
32887             this.panelSize = {width: w, height: h};
32888             this.activePanel.setSize(w, h);
32889         }
32890         if(Roo.isIE && this.tabs){
32891             this.tabs.el.repaint();
32892         }
32893     },
32894
32895     /**
32896      * Returns the container element for this region.
32897      * @return {Roo.Element}
32898      */
32899     getEl : function(){
32900         return this.el;
32901     },
32902
32903     /**
32904      * Hides this region.
32905      */
32906     hide : function(){
32907         //if(!this.collapsed){
32908             this.el.dom.style.left = "-2000px";
32909             this.el.hide();
32910         //}else{
32911          //   this.collapsedEl.dom.style.left = "-2000px";
32912          //   this.collapsedEl.hide();
32913        // }
32914         this.visible = false;
32915         this.fireEvent("visibilitychange", this, false);
32916     },
32917
32918     /**
32919      * Shows this region if it was previously hidden.
32920      */
32921     show : function(){
32922         //if(!this.collapsed){
32923             this.el.show();
32924         //}else{
32925         //    this.collapsedEl.show();
32926        // }
32927         this.visible = true;
32928         this.fireEvent("visibilitychange", this, true);
32929     },
32930 /*
32931     closeClicked : function(){
32932         if(this.activePanel){
32933             this.remove(this.activePanel);
32934         }
32935     },
32936
32937     collapseClick : function(e){
32938         if(this.isSlid){
32939            e.stopPropagation();
32940            this.slideIn();
32941         }else{
32942            e.stopPropagation();
32943            this.slideOut();
32944         }
32945     },
32946 */
32947     /**
32948      * Collapses this region.
32949      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32950      */
32951     /*
32952     collapse : function(skipAnim, skipCheck = false){
32953         if(this.collapsed) {
32954             return;
32955         }
32956         
32957         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32958             
32959             this.collapsed = true;
32960             if(this.split){
32961                 this.split.el.hide();
32962             }
32963             if(this.config.animate && skipAnim !== true){
32964                 this.fireEvent("invalidated", this);
32965                 this.animateCollapse();
32966             }else{
32967                 this.el.setLocation(-20000,-20000);
32968                 this.el.hide();
32969                 this.collapsedEl.show();
32970                 this.fireEvent("collapsed", this);
32971                 this.fireEvent("invalidated", this);
32972             }
32973         }
32974         
32975     },
32976 */
32977     animateCollapse : function(){
32978         // overridden
32979     },
32980
32981     /**
32982      * Expands this region if it was previously collapsed.
32983      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32984      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32985      */
32986     /*
32987     expand : function(e, skipAnim){
32988         if(e) {
32989             e.stopPropagation();
32990         }
32991         if(!this.collapsed || this.el.hasActiveFx()) {
32992             return;
32993         }
32994         if(this.isSlid){
32995             this.afterSlideIn();
32996             skipAnim = true;
32997         }
32998         this.collapsed = false;
32999         if(this.config.animate && skipAnim !== true){
33000             this.animateExpand();
33001         }else{
33002             this.el.show();
33003             if(this.split){
33004                 this.split.el.show();
33005             }
33006             this.collapsedEl.setLocation(-2000,-2000);
33007             this.collapsedEl.hide();
33008             this.fireEvent("invalidated", this);
33009             this.fireEvent("expanded", this);
33010         }
33011     },
33012 */
33013     animateExpand : function(){
33014         // overridden
33015     },
33016
33017     initTabs : function()
33018     {
33019         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33020         
33021         var ts = new Roo.bootstrap.panel.Tabs({
33022                 el: this.bodyEl.dom,
33023                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33024                 disableTooltips: this.config.disableTabTips,
33025                 toolbar : this.config.toolbar
33026             });
33027         
33028         if(this.config.hideTabs){
33029             ts.stripWrap.setDisplayed(false);
33030         }
33031         this.tabs = ts;
33032         ts.resizeTabs = this.config.resizeTabs === true;
33033         ts.minTabWidth = this.config.minTabWidth || 40;
33034         ts.maxTabWidth = this.config.maxTabWidth || 250;
33035         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33036         ts.monitorResize = false;
33037         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33038         ts.bodyEl.addClass('roo-layout-tabs-body');
33039         this.panels.each(this.initPanelAsTab, this);
33040     },
33041
33042     initPanelAsTab : function(panel){
33043         var ti = this.tabs.addTab(
33044                     panel.getEl().id,
33045                     panel.getTitle(),
33046                     null,
33047                     this.config.closeOnTab && panel.isClosable()
33048             );
33049         if(panel.tabTip !== undefined){
33050             ti.setTooltip(panel.tabTip);
33051         }
33052         ti.on("activate", function(){
33053               this.setActivePanel(panel);
33054         }, this);
33055         
33056         if(this.config.closeOnTab){
33057             ti.on("beforeclose", function(t, e){
33058                 e.cancel = true;
33059                 this.remove(panel);
33060             }, this);
33061         }
33062         return ti;
33063     },
33064
33065     updatePanelTitle : function(panel, title)
33066     {
33067         if(this.activePanel == panel){
33068             this.updateTitle(title);
33069         }
33070         if(this.tabs){
33071             var ti = this.tabs.getTab(panel.getEl().id);
33072             ti.setText(title);
33073             if(panel.tabTip !== undefined){
33074                 ti.setTooltip(panel.tabTip);
33075             }
33076         }
33077     },
33078
33079     updateTitle : function(title){
33080         if(this.titleTextEl && !this.config.title){
33081             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33082         }
33083     },
33084
33085     setActivePanel : function(panel)
33086     {
33087         panel = this.getPanel(panel);
33088         if(this.activePanel && this.activePanel != panel){
33089             this.activePanel.setActiveState(false);
33090         }
33091         this.activePanel = panel;
33092         panel.setActiveState(true);
33093         if(this.panelSize){
33094             panel.setSize(this.panelSize.width, this.panelSize.height);
33095         }
33096         if(this.closeBtn){
33097             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33098         }
33099         this.updateTitle(panel.getTitle());
33100         if(this.tabs){
33101             this.fireEvent("invalidated", this);
33102         }
33103         this.fireEvent("panelactivated", this, panel);
33104     },
33105
33106     /**
33107      * Shows the specified panel.
33108      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33109      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33110      */
33111     showPanel : function(panel)
33112     {
33113         panel = this.getPanel(panel);
33114         if(panel){
33115             if(this.tabs){
33116                 var tab = this.tabs.getTab(panel.getEl().id);
33117                 if(tab.isHidden()){
33118                     this.tabs.unhideTab(tab.id);
33119                 }
33120                 tab.activate();
33121             }else{
33122                 this.setActivePanel(panel);
33123             }
33124         }
33125         return panel;
33126     },
33127
33128     /**
33129      * Get the active panel for this region.
33130      * @return {Roo.ContentPanel} The active panel or null
33131      */
33132     getActivePanel : function(){
33133         return this.activePanel;
33134     },
33135
33136     validateVisibility : function(){
33137         if(this.panels.getCount() < 1){
33138             this.updateTitle("&#160;");
33139             this.closeBtn.hide();
33140             this.hide();
33141         }else{
33142             if(!this.isVisible()){
33143                 this.show();
33144             }
33145         }
33146     },
33147
33148     /**
33149      * Adds the passed ContentPanel(s) to this region.
33150      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33151      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33152      */
33153     add : function(panel)
33154     {
33155         if(arguments.length > 1){
33156             for(var i = 0, len = arguments.length; i < len; i++) {
33157                 this.add(arguments[i]);
33158             }
33159             return null;
33160         }
33161         
33162         // if we have not been rendered yet, then we can not really do much of this..
33163         if (!this.bodyEl) {
33164             this.unrendered_panels.push(panel);
33165             return panel;
33166         }
33167         
33168         
33169         
33170         
33171         if(this.hasPanel(panel)){
33172             this.showPanel(panel);
33173             return panel;
33174         }
33175         panel.setRegion(this);
33176         this.panels.add(panel);
33177        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33178             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33179             // and hide them... ???
33180             this.bodyEl.dom.appendChild(panel.getEl().dom);
33181             if(panel.background !== true){
33182                 this.setActivePanel(panel);
33183             }
33184             this.fireEvent("paneladded", this, panel);
33185             return panel;
33186         }
33187         */
33188         if(!this.tabs){
33189             this.initTabs();
33190         }else{
33191             this.initPanelAsTab(panel);
33192         }
33193         
33194         
33195         if(panel.background !== true){
33196             this.tabs.activate(panel.getEl().id);
33197         }
33198         this.fireEvent("paneladded", this, panel);
33199         return panel;
33200     },
33201
33202     /**
33203      * Hides the tab for the specified panel.
33204      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33205      */
33206     hidePanel : function(panel){
33207         if(this.tabs && (panel = this.getPanel(panel))){
33208             this.tabs.hideTab(panel.getEl().id);
33209         }
33210     },
33211
33212     /**
33213      * Unhides the tab for a previously hidden panel.
33214      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33215      */
33216     unhidePanel : function(panel){
33217         if(this.tabs && (panel = this.getPanel(panel))){
33218             this.tabs.unhideTab(panel.getEl().id);
33219         }
33220     },
33221
33222     clearPanels : function(){
33223         while(this.panels.getCount() > 0){
33224              this.remove(this.panels.first());
33225         }
33226     },
33227
33228     /**
33229      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33230      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33231      * @param {Boolean} preservePanel Overrides the config preservePanel option
33232      * @return {Roo.ContentPanel} The panel that was removed
33233      */
33234     remove : function(panel, preservePanel)
33235     {
33236         panel = this.getPanel(panel);
33237         if(!panel){
33238             return null;
33239         }
33240         var e = {};
33241         this.fireEvent("beforeremove", this, panel, e);
33242         if(e.cancel === true){
33243             return null;
33244         }
33245         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33246         var panelId = panel.getId();
33247         this.panels.removeKey(panelId);
33248         if(preservePanel){
33249             document.body.appendChild(panel.getEl().dom);
33250         }
33251         if(this.tabs){
33252             this.tabs.removeTab(panel.getEl().id);
33253         }else if (!preservePanel){
33254             this.bodyEl.dom.removeChild(panel.getEl().dom);
33255         }
33256         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33257             var p = this.panels.first();
33258             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33259             tempEl.appendChild(p.getEl().dom);
33260             this.bodyEl.update("");
33261             this.bodyEl.dom.appendChild(p.getEl().dom);
33262             tempEl = null;
33263             this.updateTitle(p.getTitle());
33264             this.tabs = null;
33265             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33266             this.setActivePanel(p);
33267         }
33268         panel.setRegion(null);
33269         if(this.activePanel == panel){
33270             this.activePanel = null;
33271         }
33272         if(this.config.autoDestroy !== false && preservePanel !== true){
33273             try{panel.destroy();}catch(e){}
33274         }
33275         this.fireEvent("panelremoved", this, panel);
33276         return panel;
33277     },
33278
33279     /**
33280      * Returns the TabPanel component used by this region
33281      * @return {Roo.TabPanel}
33282      */
33283     getTabs : function(){
33284         return this.tabs;
33285     },
33286
33287     createTool : function(parentEl, className){
33288         var btn = Roo.DomHelper.append(parentEl, {
33289             tag: "div",
33290             cls: "x-layout-tools-button",
33291             children: [ {
33292                 tag: "div",
33293                 cls: "roo-layout-tools-button-inner " + className,
33294                 html: "&#160;"
33295             }]
33296         }, true);
33297         btn.addClassOnOver("roo-layout-tools-button-over");
33298         return btn;
33299     }
33300 });/*
33301  * Based on:
33302  * Ext JS Library 1.1.1
33303  * Copyright(c) 2006-2007, Ext JS, LLC.
33304  *
33305  * Originally Released Under LGPL - original licence link has changed is not relivant.
33306  *
33307  * Fork - LGPL
33308  * <script type="text/javascript">
33309  */
33310  
33311
33312
33313 /**
33314  * @class Roo.SplitLayoutRegion
33315  * @extends Roo.LayoutRegion
33316  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33317  */
33318 Roo.bootstrap.layout.Split = function(config){
33319     this.cursor = config.cursor;
33320     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33321 };
33322
33323 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33324 {
33325     splitTip : "Drag to resize.",
33326     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33327     useSplitTips : false,
33328
33329     applyConfig : function(config){
33330         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33331     },
33332     
33333     onRender : function(ctr,pos) {
33334         
33335         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33336         if(!this.config.split){
33337             return;
33338         }
33339         if(!this.split){
33340             
33341             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33342                             tag: "div",
33343                             id: this.el.id + "-split",
33344                             cls: "roo-layout-split roo-layout-split-"+this.position,
33345                             html: "&#160;"
33346             });
33347             /** The SplitBar for this region 
33348             * @type Roo.SplitBar */
33349             // does not exist yet...
33350             Roo.log([this.position, this.orientation]);
33351             
33352             this.split = new Roo.bootstrap.SplitBar({
33353                 dragElement : splitEl,
33354                 resizingElement: this.el,
33355                 orientation : this.orientation
33356             });
33357             
33358             this.split.on("moved", this.onSplitMove, this);
33359             this.split.useShim = this.config.useShim === true;
33360             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33361             if(this.useSplitTips){
33362                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33363             }
33364             //if(config.collapsible){
33365             //    this.split.el.on("dblclick", this.collapse,  this);
33366             //}
33367         }
33368         if(typeof this.config.minSize != "undefined"){
33369             this.split.minSize = this.config.minSize;
33370         }
33371         if(typeof this.config.maxSize != "undefined"){
33372             this.split.maxSize = this.config.maxSize;
33373         }
33374         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33375             this.hideSplitter();
33376         }
33377         
33378     },
33379
33380     getHMaxSize : function(){
33381          var cmax = this.config.maxSize || 10000;
33382          var center = this.mgr.getRegion("center");
33383          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33384     },
33385
33386     getVMaxSize : function(){
33387          var cmax = this.config.maxSize || 10000;
33388          var center = this.mgr.getRegion("center");
33389          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33390     },
33391
33392     onSplitMove : function(split, newSize){
33393         this.fireEvent("resized", this, newSize);
33394     },
33395     
33396     /** 
33397      * Returns the {@link Roo.SplitBar} for this region.
33398      * @return {Roo.SplitBar}
33399      */
33400     getSplitBar : function(){
33401         return this.split;
33402     },
33403     
33404     hide : function(){
33405         this.hideSplitter();
33406         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33407     },
33408
33409     hideSplitter : function(){
33410         if(this.split){
33411             this.split.el.setLocation(-2000,-2000);
33412             this.split.el.hide();
33413         }
33414     },
33415
33416     show : function(){
33417         if(this.split){
33418             this.split.el.show();
33419         }
33420         Roo.bootstrap.layout.Split.superclass.show.call(this);
33421     },
33422     
33423     beforeSlide: function(){
33424         if(Roo.isGecko){// firefox overflow auto bug workaround
33425             this.bodyEl.clip();
33426             if(this.tabs) {
33427                 this.tabs.bodyEl.clip();
33428             }
33429             if(this.activePanel){
33430                 this.activePanel.getEl().clip();
33431                 
33432                 if(this.activePanel.beforeSlide){
33433                     this.activePanel.beforeSlide();
33434                 }
33435             }
33436         }
33437     },
33438     
33439     afterSlide : function(){
33440         if(Roo.isGecko){// firefox overflow auto bug workaround
33441             this.bodyEl.unclip();
33442             if(this.tabs) {
33443                 this.tabs.bodyEl.unclip();
33444             }
33445             if(this.activePanel){
33446                 this.activePanel.getEl().unclip();
33447                 if(this.activePanel.afterSlide){
33448                     this.activePanel.afterSlide();
33449                 }
33450             }
33451         }
33452     },
33453
33454     initAutoHide : function(){
33455         if(this.autoHide !== false){
33456             if(!this.autoHideHd){
33457                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33458                 this.autoHideHd = {
33459                     "mouseout": function(e){
33460                         if(!e.within(this.el, true)){
33461                             st.delay(500);
33462                         }
33463                     },
33464                     "mouseover" : function(e){
33465                         st.cancel();
33466                     },
33467                     scope : this
33468                 };
33469             }
33470             this.el.on(this.autoHideHd);
33471         }
33472     },
33473
33474     clearAutoHide : function(){
33475         if(this.autoHide !== false){
33476             this.el.un("mouseout", this.autoHideHd.mouseout);
33477             this.el.un("mouseover", this.autoHideHd.mouseover);
33478         }
33479     },
33480
33481     clearMonitor : function(){
33482         Roo.get(document).un("click", this.slideInIf, this);
33483     },
33484
33485     // these names are backwards but not changed for compat
33486     slideOut : function(){
33487         if(this.isSlid || this.el.hasActiveFx()){
33488             return;
33489         }
33490         this.isSlid = true;
33491         if(this.collapseBtn){
33492             this.collapseBtn.hide();
33493         }
33494         this.closeBtnState = this.closeBtn.getStyle('display');
33495         this.closeBtn.hide();
33496         if(this.stickBtn){
33497             this.stickBtn.show();
33498         }
33499         this.el.show();
33500         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33501         this.beforeSlide();
33502         this.el.setStyle("z-index", 10001);
33503         this.el.slideIn(this.getSlideAnchor(), {
33504             callback: function(){
33505                 this.afterSlide();
33506                 this.initAutoHide();
33507                 Roo.get(document).on("click", this.slideInIf, this);
33508                 this.fireEvent("slideshow", this);
33509             },
33510             scope: this,
33511             block: true
33512         });
33513     },
33514
33515     afterSlideIn : function(){
33516         this.clearAutoHide();
33517         this.isSlid = false;
33518         this.clearMonitor();
33519         this.el.setStyle("z-index", "");
33520         if(this.collapseBtn){
33521             this.collapseBtn.show();
33522         }
33523         this.closeBtn.setStyle('display', this.closeBtnState);
33524         if(this.stickBtn){
33525             this.stickBtn.hide();
33526         }
33527         this.fireEvent("slidehide", this);
33528     },
33529
33530     slideIn : function(cb){
33531         if(!this.isSlid || this.el.hasActiveFx()){
33532             Roo.callback(cb);
33533             return;
33534         }
33535         this.isSlid = false;
33536         this.beforeSlide();
33537         this.el.slideOut(this.getSlideAnchor(), {
33538             callback: function(){
33539                 this.el.setLeftTop(-10000, -10000);
33540                 this.afterSlide();
33541                 this.afterSlideIn();
33542                 Roo.callback(cb);
33543             },
33544             scope: this,
33545             block: true
33546         });
33547     },
33548     
33549     slideInIf : function(e){
33550         if(!e.within(this.el)){
33551             this.slideIn();
33552         }
33553     },
33554
33555     animateCollapse : function(){
33556         this.beforeSlide();
33557         this.el.setStyle("z-index", 20000);
33558         var anchor = this.getSlideAnchor();
33559         this.el.slideOut(anchor, {
33560             callback : function(){
33561                 this.el.setStyle("z-index", "");
33562                 this.collapsedEl.slideIn(anchor, {duration:.3});
33563                 this.afterSlide();
33564                 this.el.setLocation(-10000,-10000);
33565                 this.el.hide();
33566                 this.fireEvent("collapsed", this);
33567             },
33568             scope: this,
33569             block: true
33570         });
33571     },
33572
33573     animateExpand : function(){
33574         this.beforeSlide();
33575         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33576         this.el.setStyle("z-index", 20000);
33577         this.collapsedEl.hide({
33578             duration:.1
33579         });
33580         this.el.slideIn(this.getSlideAnchor(), {
33581             callback : function(){
33582                 this.el.setStyle("z-index", "");
33583                 this.afterSlide();
33584                 if(this.split){
33585                     this.split.el.show();
33586                 }
33587                 this.fireEvent("invalidated", this);
33588                 this.fireEvent("expanded", this);
33589             },
33590             scope: this,
33591             block: true
33592         });
33593     },
33594
33595     anchors : {
33596         "west" : "left",
33597         "east" : "right",
33598         "north" : "top",
33599         "south" : "bottom"
33600     },
33601
33602     sanchors : {
33603         "west" : "l",
33604         "east" : "r",
33605         "north" : "t",
33606         "south" : "b"
33607     },
33608
33609     canchors : {
33610         "west" : "tl-tr",
33611         "east" : "tr-tl",
33612         "north" : "tl-bl",
33613         "south" : "bl-tl"
33614     },
33615
33616     getAnchor : function(){
33617         return this.anchors[this.position];
33618     },
33619
33620     getCollapseAnchor : function(){
33621         return this.canchors[this.position];
33622     },
33623
33624     getSlideAnchor : function(){
33625         return this.sanchors[this.position];
33626     },
33627
33628     getAlignAdj : function(){
33629         var cm = this.cmargins;
33630         switch(this.position){
33631             case "west":
33632                 return [0, 0];
33633             break;
33634             case "east":
33635                 return [0, 0];
33636             break;
33637             case "north":
33638                 return [0, 0];
33639             break;
33640             case "south":
33641                 return [0, 0];
33642             break;
33643         }
33644     },
33645
33646     getExpandAdj : function(){
33647         var c = this.collapsedEl, cm = this.cmargins;
33648         switch(this.position){
33649             case "west":
33650                 return [-(cm.right+c.getWidth()+cm.left), 0];
33651             break;
33652             case "east":
33653                 return [cm.right+c.getWidth()+cm.left, 0];
33654             break;
33655             case "north":
33656                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33657             break;
33658             case "south":
33659                 return [0, cm.top+cm.bottom+c.getHeight()];
33660             break;
33661         }
33662     }
33663 });/*
33664  * Based on:
33665  * Ext JS Library 1.1.1
33666  * Copyright(c) 2006-2007, Ext JS, LLC.
33667  *
33668  * Originally Released Under LGPL - original licence link has changed is not relivant.
33669  *
33670  * Fork - LGPL
33671  * <script type="text/javascript">
33672  */
33673 /*
33674  * These classes are private internal classes
33675  */
33676 Roo.bootstrap.layout.Center = function(config){
33677     config.region = "center";
33678     Roo.bootstrap.layout.Region.call(this, config);
33679     this.visible = true;
33680     this.minWidth = config.minWidth || 20;
33681     this.minHeight = config.minHeight || 20;
33682 };
33683
33684 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33685     hide : function(){
33686         // center panel can't be hidden
33687     },
33688     
33689     show : function(){
33690         // center panel can't be hidden
33691     },
33692     
33693     getMinWidth: function(){
33694         return this.minWidth;
33695     },
33696     
33697     getMinHeight: function(){
33698         return this.minHeight;
33699     }
33700 });
33701
33702
33703
33704
33705  
33706
33707
33708
33709
33710
33711 Roo.bootstrap.layout.North = function(config)
33712 {
33713     config.region = 'north';
33714     config.cursor = 'n-resize';
33715     
33716     Roo.bootstrap.layout.Split.call(this, config);
33717     
33718     
33719     if(this.split){
33720         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33721         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33722         this.split.el.addClass("roo-layout-split-v");
33723     }
33724     var size = config.initialSize || config.height;
33725     if(typeof size != "undefined"){
33726         this.el.setHeight(size);
33727     }
33728 };
33729 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33730 {
33731     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33732     
33733     
33734     
33735     getBox : function(){
33736         if(this.collapsed){
33737             return this.collapsedEl.getBox();
33738         }
33739         var box = this.el.getBox();
33740         if(this.split){
33741             box.height += this.split.el.getHeight();
33742         }
33743         return box;
33744     },
33745     
33746     updateBox : function(box){
33747         if(this.split && !this.collapsed){
33748             box.height -= this.split.el.getHeight();
33749             this.split.el.setLeft(box.x);
33750             this.split.el.setTop(box.y+box.height);
33751             this.split.el.setWidth(box.width);
33752         }
33753         if(this.collapsed){
33754             this.updateBody(box.width, null);
33755         }
33756         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33757     }
33758 });
33759
33760
33761
33762
33763
33764 Roo.bootstrap.layout.South = function(config){
33765     config.region = 'south';
33766     config.cursor = 's-resize';
33767     Roo.bootstrap.layout.Split.call(this, config);
33768     if(this.split){
33769         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33770         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33771         this.split.el.addClass("roo-layout-split-v");
33772     }
33773     var size = config.initialSize || config.height;
33774     if(typeof size != "undefined"){
33775         this.el.setHeight(size);
33776     }
33777 };
33778
33779 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33780     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33781     getBox : function(){
33782         if(this.collapsed){
33783             return this.collapsedEl.getBox();
33784         }
33785         var box = this.el.getBox();
33786         if(this.split){
33787             var sh = this.split.el.getHeight();
33788             box.height += sh;
33789             box.y -= sh;
33790         }
33791         return box;
33792     },
33793     
33794     updateBox : function(box){
33795         if(this.split && !this.collapsed){
33796             var sh = this.split.el.getHeight();
33797             box.height -= sh;
33798             box.y += sh;
33799             this.split.el.setLeft(box.x);
33800             this.split.el.setTop(box.y-sh);
33801             this.split.el.setWidth(box.width);
33802         }
33803         if(this.collapsed){
33804             this.updateBody(box.width, null);
33805         }
33806         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33807     }
33808 });
33809
33810 Roo.bootstrap.layout.East = function(config){
33811     config.region = "east";
33812     config.cursor = "e-resize";
33813     Roo.bootstrap.layout.Split.call(this, config);
33814     if(this.split){
33815         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33816         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33817         this.split.el.addClass("roo-layout-split-h");
33818     }
33819     var size = config.initialSize || config.width;
33820     if(typeof size != "undefined"){
33821         this.el.setWidth(size);
33822     }
33823 };
33824 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33825     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33826     getBox : function(){
33827         if(this.collapsed){
33828             return this.collapsedEl.getBox();
33829         }
33830         var box = this.el.getBox();
33831         if(this.split){
33832             var sw = this.split.el.getWidth();
33833             box.width += sw;
33834             box.x -= sw;
33835         }
33836         return box;
33837     },
33838
33839     updateBox : function(box){
33840         if(this.split && !this.collapsed){
33841             var sw = this.split.el.getWidth();
33842             box.width -= sw;
33843             this.split.el.setLeft(box.x);
33844             this.split.el.setTop(box.y);
33845             this.split.el.setHeight(box.height);
33846             box.x += sw;
33847         }
33848         if(this.collapsed){
33849             this.updateBody(null, box.height);
33850         }
33851         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33852     }
33853 });
33854
33855 Roo.bootstrap.layout.West = function(config){
33856     config.region = "west";
33857     config.cursor = "w-resize";
33858     
33859     Roo.bootstrap.layout.Split.call(this, config);
33860     if(this.split){
33861         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33862         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33863         this.split.el.addClass("roo-layout-split-h");
33864     }
33865     
33866 };
33867 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33868     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33869     
33870     onRender: function(ctr, pos)
33871     {
33872         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33873         var size = this.config.initialSize || this.config.width;
33874         if(typeof size != "undefined"){
33875             this.el.setWidth(size);
33876         }
33877     },
33878     
33879     getBox : function(){
33880         if(this.collapsed){
33881             return this.collapsedEl.getBox();
33882         }
33883         var box = this.el.getBox();
33884         if(this.split){
33885             box.width += this.split.el.getWidth();
33886         }
33887         return box;
33888     },
33889     
33890     updateBox : function(box){
33891         if(this.split && !this.collapsed){
33892             var sw = this.split.el.getWidth();
33893             box.width -= sw;
33894             this.split.el.setLeft(box.x+box.width);
33895             this.split.el.setTop(box.y);
33896             this.split.el.setHeight(box.height);
33897         }
33898         if(this.collapsed){
33899             this.updateBody(null, box.height);
33900         }
33901         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33902     }
33903 });
33904 Roo.namespace("Roo.bootstrap.panel");/*
33905  * Based on:
33906  * Ext JS Library 1.1.1
33907  * Copyright(c) 2006-2007, Ext JS, LLC.
33908  *
33909  * Originally Released Under LGPL - original licence link has changed is not relivant.
33910  *
33911  * Fork - LGPL
33912  * <script type="text/javascript">
33913  */
33914 /**
33915  * @class Roo.ContentPanel
33916  * @extends Roo.util.Observable
33917  * A basic ContentPanel element.
33918  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33919  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33920  * @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
33921  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33922  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33923  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33924  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33925  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33926  * @cfg {String} title          The title for this panel
33927  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33928  * @cfg {String} url            Calls {@link #setUrl} with this value
33929  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33930  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33931  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33932  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33933
33934  * @constructor
33935  * Create a new ContentPanel.
33936  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33937  * @param {String/Object} config A string to set only the title or a config object
33938  * @param {String} content (optional) Set the HTML content for this panel
33939  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33940  */
33941 Roo.bootstrap.panel.Content = function( config){
33942     
33943     var el = config.el;
33944     var content = config.content;
33945
33946     if(config.autoCreate){ // xtype is available if this is called from factory
33947         el = Roo.id();
33948     }
33949     this.el = Roo.get(el);
33950     if(!this.el && config && config.autoCreate){
33951         if(typeof config.autoCreate == "object"){
33952             if(!config.autoCreate.id){
33953                 config.autoCreate.id = config.id||el;
33954             }
33955             this.el = Roo.DomHelper.append(document.body,
33956                         config.autoCreate, true);
33957         }else{
33958             var elcfg =  {   tag: "div",
33959                             cls: "roo-layout-inactive-content",
33960                             id: config.id||el
33961                             };
33962             if (config.html) {
33963                 elcfg.html = config.html;
33964                 
33965             }
33966                         
33967             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33968         }
33969     } 
33970     this.closable = false;
33971     this.loaded = false;
33972     this.active = false;
33973    
33974       
33975     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33976         
33977         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33978         
33979         this.wrapEl = this.el.wrap();
33980         var ti = [];
33981         if (config.toolbar.items) {
33982             ti = config.toolbar.items ;
33983             delete config.toolbar.items ;
33984         }
33985         
33986         var nitems = [];
33987         this.toolbar.render(this.wrapEl, 'before');
33988         for(var i =0;i < ti.length;i++) {
33989           //  Roo.log(['add child', items[i]]);
33990             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33991         }
33992         this.toolbar.items = nitems;
33993         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33994         delete config.toolbar;
33995         
33996     }
33997     /*
33998     // xtype created footer. - not sure if will work as we normally have to render first..
33999     if (this.footer && !this.footer.el && this.footer.xtype) {
34000         if (!this.wrapEl) {
34001             this.wrapEl = this.el.wrap();
34002         }
34003     
34004         this.footer.container = this.wrapEl.createChild();
34005          
34006         this.footer = Roo.factory(this.footer, Roo);
34007         
34008     }
34009     */
34010     
34011      if(typeof config == "string"){
34012         this.title = config;
34013     }else{
34014         Roo.apply(this, config);
34015     }
34016     
34017     if(this.resizeEl){
34018         this.resizeEl = Roo.get(this.resizeEl, true);
34019     }else{
34020         this.resizeEl = this.el;
34021     }
34022     // handle view.xtype
34023     
34024  
34025     
34026     
34027     this.addEvents({
34028         /**
34029          * @event activate
34030          * Fires when this panel is activated. 
34031          * @param {Roo.ContentPanel} this
34032          */
34033         "activate" : true,
34034         /**
34035          * @event deactivate
34036          * Fires when this panel is activated. 
34037          * @param {Roo.ContentPanel} this
34038          */
34039         "deactivate" : true,
34040
34041         /**
34042          * @event resize
34043          * Fires when this panel is resized if fitToFrame is true.
34044          * @param {Roo.ContentPanel} this
34045          * @param {Number} width The width after any component adjustments
34046          * @param {Number} height The height after any component adjustments
34047          */
34048         "resize" : true,
34049         
34050          /**
34051          * @event render
34052          * Fires when this tab is created
34053          * @param {Roo.ContentPanel} this
34054          */
34055         "render" : true
34056         
34057         
34058         
34059     });
34060     
34061
34062     
34063     
34064     if(this.autoScroll){
34065         this.resizeEl.setStyle("overflow", "auto");
34066     } else {
34067         // fix randome scrolling
34068         //this.el.on('scroll', function() {
34069         //    Roo.log('fix random scolling');
34070         //    this.scrollTo('top',0); 
34071         //});
34072     }
34073     content = content || this.content;
34074     if(content){
34075         this.setContent(content);
34076     }
34077     if(config && config.url){
34078         this.setUrl(this.url, this.params, this.loadOnce);
34079     }
34080     
34081     
34082     
34083     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34084     
34085     if (this.view && typeof(this.view.xtype) != 'undefined') {
34086         this.view.el = this.el.appendChild(document.createElement("div"));
34087         this.view = Roo.factory(this.view); 
34088         this.view.render  &&  this.view.render(false, '');  
34089     }
34090     
34091     
34092     this.fireEvent('render', this);
34093 };
34094
34095 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34096     tabTip:'',
34097     setRegion : function(region){
34098         this.region = region;
34099         if(region){
34100            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34101         }else{
34102            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34103         } 
34104     },
34105     
34106     /**
34107      * Returns the toolbar for this Panel if one was configured. 
34108      * @return {Roo.Toolbar} 
34109      */
34110     getToolbar : function(){
34111         return this.toolbar;
34112     },
34113     
34114     setActiveState : function(active){
34115         this.active = active;
34116         if(!active){
34117             this.fireEvent("deactivate", this);
34118         }else{
34119             this.fireEvent("activate", this);
34120         }
34121     },
34122     /**
34123      * Updates this panel's element
34124      * @param {String} content The new content
34125      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34126     */
34127     setContent : function(content, loadScripts){
34128         this.el.update(content, loadScripts);
34129     },
34130
34131     ignoreResize : function(w, h){
34132         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34133             return true;
34134         }else{
34135             this.lastSize = {width: w, height: h};
34136             return false;
34137         }
34138     },
34139     /**
34140      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34141      * @return {Roo.UpdateManager} The UpdateManager
34142      */
34143     getUpdateManager : function(){
34144         return this.el.getUpdateManager();
34145     },
34146      /**
34147      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34148      * @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:
34149 <pre><code>
34150 panel.load({
34151     url: "your-url.php",
34152     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34153     callback: yourFunction,
34154     scope: yourObject, //(optional scope)
34155     discardUrl: false,
34156     nocache: false,
34157     text: "Loading...",
34158     timeout: 30,
34159     scripts: false
34160 });
34161 </code></pre>
34162      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34163      * 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.
34164      * @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}
34165      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34166      * @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.
34167      * @return {Roo.ContentPanel} this
34168      */
34169     load : function(){
34170         var um = this.el.getUpdateManager();
34171         um.update.apply(um, arguments);
34172         return this;
34173     },
34174
34175
34176     /**
34177      * 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.
34178      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34179      * @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)
34180      * @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)
34181      * @return {Roo.UpdateManager} The UpdateManager
34182      */
34183     setUrl : function(url, params, loadOnce){
34184         if(this.refreshDelegate){
34185             this.removeListener("activate", this.refreshDelegate);
34186         }
34187         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34188         this.on("activate", this.refreshDelegate);
34189         return this.el.getUpdateManager();
34190     },
34191     
34192     _handleRefresh : function(url, params, loadOnce){
34193         if(!loadOnce || !this.loaded){
34194             var updater = this.el.getUpdateManager();
34195             updater.update(url, params, this._setLoaded.createDelegate(this));
34196         }
34197     },
34198     
34199     _setLoaded : function(){
34200         this.loaded = true;
34201     }, 
34202     
34203     /**
34204      * Returns this panel's id
34205      * @return {String} 
34206      */
34207     getId : function(){
34208         return this.el.id;
34209     },
34210     
34211     /** 
34212      * Returns this panel's element - used by regiosn to add.
34213      * @return {Roo.Element} 
34214      */
34215     getEl : function(){
34216         return this.wrapEl || this.el;
34217     },
34218     
34219    
34220     
34221     adjustForComponents : function(width, height)
34222     {
34223         //Roo.log('adjustForComponents ');
34224         if(this.resizeEl != this.el){
34225             width -= this.el.getFrameWidth('lr');
34226             height -= this.el.getFrameWidth('tb');
34227         }
34228         if(this.toolbar){
34229             var te = this.toolbar.getEl();
34230             height -= te.getHeight();
34231             te.setWidth(width);
34232         }
34233         if(this.footer){
34234             var te = this.footer.getEl();
34235             Roo.log("footer:" + te.getHeight());
34236             
34237             height -= te.getHeight();
34238             te.setWidth(width);
34239         }
34240         
34241         
34242         if(this.adjustments){
34243             width += this.adjustments[0];
34244             height += this.adjustments[1];
34245         }
34246         return {"width": width, "height": height};
34247     },
34248     
34249     setSize : function(width, height){
34250         if(this.fitToFrame && !this.ignoreResize(width, height)){
34251             if(this.fitContainer && this.resizeEl != this.el){
34252                 this.el.setSize(width, height);
34253             }
34254             var size = this.adjustForComponents(width, height);
34255             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34256             this.fireEvent('resize', this, size.width, size.height);
34257         }
34258     },
34259     
34260     /**
34261      * Returns this panel's title
34262      * @return {String} 
34263      */
34264     getTitle : function(){
34265         return this.title;
34266     },
34267     
34268     /**
34269      * Set this panel's title
34270      * @param {String} title
34271      */
34272     setTitle : function(title){
34273         this.title = title;
34274         if(this.region){
34275             this.region.updatePanelTitle(this, title);
34276         }
34277     },
34278     
34279     /**
34280      * Returns true is this panel was configured to be closable
34281      * @return {Boolean} 
34282      */
34283     isClosable : function(){
34284         return this.closable;
34285     },
34286     
34287     beforeSlide : function(){
34288         this.el.clip();
34289         this.resizeEl.clip();
34290     },
34291     
34292     afterSlide : function(){
34293         this.el.unclip();
34294         this.resizeEl.unclip();
34295     },
34296     
34297     /**
34298      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34299      *   Will fail silently if the {@link #setUrl} method has not been called.
34300      *   This does not activate the panel, just updates its content.
34301      */
34302     refresh : function(){
34303         if(this.refreshDelegate){
34304            this.loaded = false;
34305            this.refreshDelegate();
34306         }
34307     },
34308     
34309     /**
34310      * Destroys this panel
34311      */
34312     destroy : function(){
34313         this.el.removeAllListeners();
34314         var tempEl = document.createElement("span");
34315         tempEl.appendChild(this.el.dom);
34316         tempEl.innerHTML = "";
34317         this.el.remove();
34318         this.el = null;
34319     },
34320     
34321     /**
34322      * form - if the content panel contains a form - this is a reference to it.
34323      * @type {Roo.form.Form}
34324      */
34325     form : false,
34326     /**
34327      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34328      *    This contains a reference to it.
34329      * @type {Roo.View}
34330      */
34331     view : false,
34332     
34333       /**
34334      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34335      * <pre><code>
34336
34337 layout.addxtype({
34338        xtype : 'Form',
34339        items: [ .... ]
34340    }
34341 );
34342
34343 </code></pre>
34344      * @param {Object} cfg Xtype definition of item to add.
34345      */
34346     
34347     
34348     getChildContainer: function () {
34349         return this.getEl();
34350     }
34351     
34352     
34353     /*
34354         var  ret = new Roo.factory(cfg);
34355         return ret;
34356         
34357         
34358         // add form..
34359         if (cfg.xtype.match(/^Form$/)) {
34360             
34361             var el;
34362             //if (this.footer) {
34363             //    el = this.footer.container.insertSibling(false, 'before');
34364             //} else {
34365                 el = this.el.createChild();
34366             //}
34367
34368             this.form = new  Roo.form.Form(cfg);
34369             
34370             
34371             if ( this.form.allItems.length) {
34372                 this.form.render(el.dom);
34373             }
34374             return this.form;
34375         }
34376         // should only have one of theses..
34377         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34378             // views.. should not be just added - used named prop 'view''
34379             
34380             cfg.el = this.el.appendChild(document.createElement("div"));
34381             // factory?
34382             
34383             var ret = new Roo.factory(cfg);
34384              
34385              ret.render && ret.render(false, ''); // render blank..
34386             this.view = ret;
34387             return ret;
34388         }
34389         return false;
34390     }
34391     \*/
34392 });
34393  
34394 /**
34395  * @class Roo.bootstrap.panel.Grid
34396  * @extends Roo.bootstrap.panel.Content
34397  * @constructor
34398  * Create a new GridPanel.
34399  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34400  * @param {Object} config A the config object
34401   
34402  */
34403
34404
34405
34406 Roo.bootstrap.panel.Grid = function(config)
34407 {
34408     
34409       
34410     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34411         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34412
34413     config.el = this.wrapper;
34414     //this.el = this.wrapper;
34415     
34416       if (config.container) {
34417         // ctor'ed from a Border/panel.grid
34418         
34419         
34420         this.wrapper.setStyle("overflow", "hidden");
34421         this.wrapper.addClass('roo-grid-container');
34422
34423     }
34424     
34425     
34426     if(config.toolbar){
34427         var tool_el = this.wrapper.createChild();    
34428         this.toolbar = Roo.factory(config.toolbar);
34429         var ti = [];
34430         if (config.toolbar.items) {
34431             ti = config.toolbar.items ;
34432             delete config.toolbar.items ;
34433         }
34434         
34435         var nitems = [];
34436         this.toolbar.render(tool_el);
34437         for(var i =0;i < ti.length;i++) {
34438           //  Roo.log(['add child', items[i]]);
34439             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34440         }
34441         this.toolbar.items = nitems;
34442         
34443         delete config.toolbar;
34444     }
34445     
34446     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34447     config.grid.scrollBody = true;;
34448     config.grid.monitorWindowResize = false; // turn off autosizing
34449     config.grid.autoHeight = false;
34450     config.grid.autoWidth = false;
34451     
34452     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34453     
34454     if (config.background) {
34455         // render grid on panel activation (if panel background)
34456         this.on('activate', function(gp) {
34457             if (!gp.grid.rendered) {
34458                 gp.grid.render(el);
34459                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34460
34461             }
34462         });
34463             
34464     } else {
34465         this.grid.render(this.wrapper);
34466         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34467
34468     }
34469     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34470     // ??? needed ??? config.el = this.wrapper;
34471     
34472     
34473     
34474   
34475     // xtype created footer. - not sure if will work as we normally have to render first..
34476     if (this.footer && !this.footer.el && this.footer.xtype) {
34477         
34478         var ctr = this.grid.getView().getFooterPanel(true);
34479         this.footer.dataSource = this.grid.dataSource;
34480         this.footer = Roo.factory(this.footer, Roo);
34481         this.footer.render(ctr);
34482         
34483     }
34484     
34485     
34486     
34487     
34488      
34489 };
34490
34491 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34492     getId : function(){
34493         return this.grid.id;
34494     },
34495     
34496     /**
34497      * Returns the grid for this panel
34498      * @return {Roo.bootstrap.Table} 
34499      */
34500     getGrid : function(){
34501         return this.grid;    
34502     },
34503     
34504     setSize : function(width, height){
34505         if(!this.ignoreResize(width, height)){
34506             var grid = this.grid;
34507             var size = this.adjustForComponents(width, height);
34508             var gridel = grid.getGridEl();
34509             gridel.setSize(size.width, size.height);
34510             /*
34511             var thd = grid.getGridEl().select('thead',true).first();
34512             var tbd = grid.getGridEl().select('tbody', true).first();
34513             if (tbd) {
34514                 tbd.setSize(width, height - thd.getHeight());
34515             }
34516             */
34517             grid.autoSize();
34518         }
34519     },
34520      
34521     
34522     
34523     beforeSlide : function(){
34524         this.grid.getView().scroller.clip();
34525     },
34526     
34527     afterSlide : function(){
34528         this.grid.getView().scroller.unclip();
34529     },
34530     
34531     destroy : function(){
34532         this.grid.destroy();
34533         delete this.grid;
34534         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34535     }
34536 });
34537
34538 /**
34539  * @class Roo.bootstrap.panel.Nest
34540  * @extends Roo.bootstrap.panel.Content
34541  * @constructor
34542  * Create a new Panel, that can contain a layout.Border.
34543  * 
34544  * 
34545  * @param {Roo.BorderLayout} layout The layout for this panel
34546  * @param {String/Object} config A string to set only the title or a config object
34547  */
34548 Roo.bootstrap.panel.Nest = function(config)
34549 {
34550     // construct with only one argument..
34551     /* FIXME - implement nicer consturctors
34552     if (layout.layout) {
34553         config = layout;
34554         layout = config.layout;
34555         delete config.layout;
34556     }
34557     if (layout.xtype && !layout.getEl) {
34558         // then layout needs constructing..
34559         layout = Roo.factory(layout, Roo);
34560     }
34561     */
34562     
34563     config.el =  config.layout.getEl();
34564     
34565     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34566     
34567     config.layout.monitorWindowResize = false; // turn off autosizing
34568     this.layout = config.layout;
34569     this.layout.getEl().addClass("roo-layout-nested-layout");
34570     
34571     
34572     
34573     
34574 };
34575
34576 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34577
34578     setSize : function(width, height){
34579         if(!this.ignoreResize(width, height)){
34580             var size = this.adjustForComponents(width, height);
34581             var el = this.layout.getEl();
34582             el.setSize(size.width, size.height);
34583             var touch = el.dom.offsetWidth;
34584             this.layout.layout();
34585             // ie requires a double layout on the first pass
34586             if(Roo.isIE && !this.initialized){
34587                 this.initialized = true;
34588                 this.layout.layout();
34589             }
34590         }
34591     },
34592     
34593     // activate all subpanels if not currently active..
34594     
34595     setActiveState : function(active){
34596         this.active = active;
34597         if(!active){
34598             this.fireEvent("deactivate", this);
34599             return;
34600         }
34601         
34602         this.fireEvent("activate", this);
34603         // not sure if this should happen before or after..
34604         if (!this.layout) {
34605             return; // should not happen..
34606         }
34607         var reg = false;
34608         for (var r in this.layout.regions) {
34609             reg = this.layout.getRegion(r);
34610             if (reg.getActivePanel()) {
34611                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34612                 reg.setActivePanel(reg.getActivePanel());
34613                 continue;
34614             }
34615             if (!reg.panels.length) {
34616                 continue;
34617             }
34618             reg.showPanel(reg.getPanel(0));
34619         }
34620         
34621         
34622         
34623         
34624     },
34625     
34626     /**
34627      * Returns the nested BorderLayout for this panel
34628      * @return {Roo.BorderLayout} 
34629      */
34630     getLayout : function(){
34631         return this.layout;
34632     },
34633     
34634      /**
34635      * Adds a xtype elements to the layout of the nested panel
34636      * <pre><code>
34637
34638 panel.addxtype({
34639        xtype : 'ContentPanel',
34640        region: 'west',
34641        items: [ .... ]
34642    }
34643 );
34644
34645 panel.addxtype({
34646         xtype : 'NestedLayoutPanel',
34647         region: 'west',
34648         layout: {
34649            center: { },
34650            west: { }   
34651         },
34652         items : [ ... list of content panels or nested layout panels.. ]
34653    }
34654 );
34655 </code></pre>
34656      * @param {Object} cfg Xtype definition of item to add.
34657      */
34658     addxtype : function(cfg) {
34659         return this.layout.addxtype(cfg);
34660     
34661     }
34662 });        /*
34663  * Based on:
34664  * Ext JS Library 1.1.1
34665  * Copyright(c) 2006-2007, Ext JS, LLC.
34666  *
34667  * Originally Released Under LGPL - original licence link has changed is not relivant.
34668  *
34669  * Fork - LGPL
34670  * <script type="text/javascript">
34671  */
34672 /**
34673  * @class Roo.TabPanel
34674  * @extends Roo.util.Observable
34675  * A lightweight tab container.
34676  * <br><br>
34677  * Usage:
34678  * <pre><code>
34679 // basic tabs 1, built from existing content
34680 var tabs = new Roo.TabPanel("tabs1");
34681 tabs.addTab("script", "View Script");
34682 tabs.addTab("markup", "View Markup");
34683 tabs.activate("script");
34684
34685 // more advanced tabs, built from javascript
34686 var jtabs = new Roo.TabPanel("jtabs");
34687 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34688
34689 // set up the UpdateManager
34690 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34691 var updater = tab2.getUpdateManager();
34692 updater.setDefaultUrl("ajax1.htm");
34693 tab2.on('activate', updater.refresh, updater, true);
34694
34695 // Use setUrl for Ajax loading
34696 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34697 tab3.setUrl("ajax2.htm", null, true);
34698
34699 // Disabled tab
34700 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34701 tab4.disable();
34702
34703 jtabs.activate("jtabs-1");
34704  * </code></pre>
34705  * @constructor
34706  * Create a new TabPanel.
34707  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34708  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34709  */
34710 Roo.bootstrap.panel.Tabs = function(config){
34711     /**
34712     * The container element for this TabPanel.
34713     * @type Roo.Element
34714     */
34715     this.el = Roo.get(config.el);
34716     delete config.el;
34717     if(config){
34718         if(typeof config == "boolean"){
34719             this.tabPosition = config ? "bottom" : "top";
34720         }else{
34721             Roo.apply(this, config);
34722         }
34723     }
34724     
34725     if(this.tabPosition == "bottom"){
34726         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34727         this.el.addClass("roo-tabs-bottom");
34728     }
34729     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34730     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34731     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34732     if(Roo.isIE){
34733         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34734     }
34735     if(this.tabPosition != "bottom"){
34736         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34737          * @type Roo.Element
34738          */
34739         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34740         this.el.addClass("roo-tabs-top");
34741     }
34742     this.items = [];
34743
34744     this.bodyEl.setStyle("position", "relative");
34745
34746     this.active = null;
34747     this.activateDelegate = this.activate.createDelegate(this);
34748
34749     this.addEvents({
34750         /**
34751          * @event tabchange
34752          * Fires when the active tab changes
34753          * @param {Roo.TabPanel} this
34754          * @param {Roo.TabPanelItem} activePanel The new active tab
34755          */
34756         "tabchange": true,
34757         /**
34758          * @event beforetabchange
34759          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34760          * @param {Roo.TabPanel} this
34761          * @param {Object} e Set cancel to true on this object to cancel the tab change
34762          * @param {Roo.TabPanelItem} tab The tab being changed to
34763          */
34764         "beforetabchange" : true
34765     });
34766
34767     Roo.EventManager.onWindowResize(this.onResize, this);
34768     this.cpad = this.el.getPadding("lr");
34769     this.hiddenCount = 0;
34770
34771
34772     // toolbar on the tabbar support...
34773     if (this.toolbar) {
34774         alert("no toolbar support yet");
34775         this.toolbar  = false;
34776         /*
34777         var tcfg = this.toolbar;
34778         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34779         this.toolbar = new Roo.Toolbar(tcfg);
34780         if (Roo.isSafari) {
34781             var tbl = tcfg.container.child('table', true);
34782             tbl.setAttribute('width', '100%');
34783         }
34784         */
34785         
34786     }
34787    
34788
34789
34790     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34791 };
34792
34793 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34794     /*
34795      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34796      */
34797     tabPosition : "top",
34798     /*
34799      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34800      */
34801     currentTabWidth : 0,
34802     /*
34803      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34804      */
34805     minTabWidth : 40,
34806     /*
34807      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34808      */
34809     maxTabWidth : 250,
34810     /*
34811      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34812      */
34813     preferredTabWidth : 175,
34814     /*
34815      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34816      */
34817     resizeTabs : false,
34818     /*
34819      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34820      */
34821     monitorResize : true,
34822     /*
34823      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34824      */
34825     toolbar : false,
34826
34827     /**
34828      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34829      * @param {String} id The id of the div to use <b>or create</b>
34830      * @param {String} text The text for the tab
34831      * @param {String} content (optional) Content to put in the TabPanelItem body
34832      * @param {Boolean} closable (optional) True to create a close icon on the tab
34833      * @return {Roo.TabPanelItem} The created TabPanelItem
34834      */
34835     addTab : function(id, text, content, closable)
34836     {
34837         var item = new Roo.bootstrap.panel.TabItem({
34838             panel: this,
34839             id : id,
34840             text : text,
34841             closable : closable
34842         });
34843         this.addTabItem(item);
34844         if(content){
34845             item.setContent(content);
34846         }
34847         return item;
34848     },
34849
34850     /**
34851      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34852      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34853      * @return {Roo.TabPanelItem}
34854      */
34855     getTab : function(id){
34856         return this.items[id];
34857     },
34858
34859     /**
34860      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34861      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34862      */
34863     hideTab : function(id){
34864         var t = this.items[id];
34865         if(!t.isHidden()){
34866            t.setHidden(true);
34867            this.hiddenCount++;
34868            this.autoSizeTabs();
34869         }
34870     },
34871
34872     /**
34873      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34874      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34875      */
34876     unhideTab : function(id){
34877         var t = this.items[id];
34878         if(t.isHidden()){
34879            t.setHidden(false);
34880            this.hiddenCount--;
34881            this.autoSizeTabs();
34882         }
34883     },
34884
34885     /**
34886      * Adds an existing {@link Roo.TabPanelItem}.
34887      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34888      */
34889     addTabItem : function(item){
34890         this.items[item.id] = item;
34891         this.items.push(item);
34892       //  if(this.resizeTabs){
34893     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34894   //         this.autoSizeTabs();
34895 //        }else{
34896 //            item.autoSize();
34897        // }
34898     },
34899
34900     /**
34901      * Removes a {@link Roo.TabPanelItem}.
34902      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34903      */
34904     removeTab : function(id){
34905         var items = this.items;
34906         var tab = items[id];
34907         if(!tab) { return; }
34908         var index = items.indexOf(tab);
34909         if(this.active == tab && items.length > 1){
34910             var newTab = this.getNextAvailable(index);
34911             if(newTab) {
34912                 newTab.activate();
34913             }
34914         }
34915         this.stripEl.dom.removeChild(tab.pnode.dom);
34916         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34917             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34918         }
34919         items.splice(index, 1);
34920         delete this.items[tab.id];
34921         tab.fireEvent("close", tab);
34922         tab.purgeListeners();
34923         this.autoSizeTabs();
34924     },
34925
34926     getNextAvailable : function(start){
34927         var items = this.items;
34928         var index = start;
34929         // look for a next tab that will slide over to
34930         // replace the one being removed
34931         while(index < items.length){
34932             var item = items[++index];
34933             if(item && !item.isHidden()){
34934                 return item;
34935             }
34936         }
34937         // if one isn't found select the previous tab (on the left)
34938         index = start;
34939         while(index >= 0){
34940             var item = items[--index];
34941             if(item && !item.isHidden()){
34942                 return item;
34943             }
34944         }
34945         return null;
34946     },
34947
34948     /**
34949      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34950      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34951      */
34952     disableTab : function(id){
34953         var tab = this.items[id];
34954         if(tab && this.active != tab){
34955             tab.disable();
34956         }
34957     },
34958
34959     /**
34960      * Enables a {@link Roo.TabPanelItem} that is disabled.
34961      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34962      */
34963     enableTab : function(id){
34964         var tab = this.items[id];
34965         tab.enable();
34966     },
34967
34968     /**
34969      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34970      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34971      * @return {Roo.TabPanelItem} The TabPanelItem.
34972      */
34973     activate : function(id){
34974         var tab = this.items[id];
34975         if(!tab){
34976             return null;
34977         }
34978         if(tab == this.active || tab.disabled){
34979             return tab;
34980         }
34981         var e = {};
34982         this.fireEvent("beforetabchange", this, e, tab);
34983         if(e.cancel !== true && !tab.disabled){
34984             if(this.active){
34985                 this.active.hide();
34986             }
34987             this.active = this.items[id];
34988             this.active.show();
34989             this.fireEvent("tabchange", this, this.active);
34990         }
34991         return tab;
34992     },
34993
34994     /**
34995      * Gets the active {@link Roo.TabPanelItem}.
34996      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34997      */
34998     getActiveTab : function(){
34999         return this.active;
35000     },
35001
35002     /**
35003      * Updates the tab body element to fit the height of the container element
35004      * for overflow scrolling
35005      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35006      */
35007     syncHeight : function(targetHeight){
35008         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35009         var bm = this.bodyEl.getMargins();
35010         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35011         this.bodyEl.setHeight(newHeight);
35012         return newHeight;
35013     },
35014
35015     onResize : function(){
35016         if(this.monitorResize){
35017             this.autoSizeTabs();
35018         }
35019     },
35020
35021     /**
35022      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35023      */
35024     beginUpdate : function(){
35025         this.updating = true;
35026     },
35027
35028     /**
35029      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35030      */
35031     endUpdate : function(){
35032         this.updating = false;
35033         this.autoSizeTabs();
35034     },
35035
35036     /**
35037      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35038      */
35039     autoSizeTabs : function(){
35040         var count = this.items.length;
35041         var vcount = count - this.hiddenCount;
35042         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35043             return;
35044         }
35045         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35046         var availWidth = Math.floor(w / vcount);
35047         var b = this.stripBody;
35048         if(b.getWidth() > w){
35049             var tabs = this.items;
35050             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35051             if(availWidth < this.minTabWidth){
35052                 /*if(!this.sleft){    // incomplete scrolling code
35053                     this.createScrollButtons();
35054                 }
35055                 this.showScroll();
35056                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35057             }
35058         }else{
35059             if(this.currentTabWidth < this.preferredTabWidth){
35060                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35061             }
35062         }
35063     },
35064
35065     /**
35066      * Returns the number of tabs in this TabPanel.
35067      * @return {Number}
35068      */
35069      getCount : function(){
35070          return this.items.length;
35071      },
35072
35073     /**
35074      * Resizes all the tabs to the passed width
35075      * @param {Number} The new width
35076      */
35077     setTabWidth : function(width){
35078         this.currentTabWidth = width;
35079         for(var i = 0, len = this.items.length; i < len; i++) {
35080                 if(!this.items[i].isHidden()) {
35081                 this.items[i].setWidth(width);
35082             }
35083         }
35084     },
35085
35086     /**
35087      * Destroys this TabPanel
35088      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35089      */
35090     destroy : function(removeEl){
35091         Roo.EventManager.removeResizeListener(this.onResize, this);
35092         for(var i = 0, len = this.items.length; i < len; i++){
35093             this.items[i].purgeListeners();
35094         }
35095         if(removeEl === true){
35096             this.el.update("");
35097             this.el.remove();
35098         }
35099     },
35100     
35101     createStrip : function(container)
35102     {
35103         var strip = document.createElement("nav");
35104         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35105         container.appendChild(strip);
35106         return strip;
35107     },
35108     
35109     createStripList : function(strip)
35110     {
35111         // div wrapper for retard IE
35112         // returns the "tr" element.
35113         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35114         //'<div class="x-tabs-strip-wrap">'+
35115           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35116           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35117         return strip.firstChild; //.firstChild.firstChild.firstChild;
35118     },
35119     createBody : function(container)
35120     {
35121         var body = document.createElement("div");
35122         Roo.id(body, "tab-body");
35123         //Roo.fly(body).addClass("x-tabs-body");
35124         Roo.fly(body).addClass("tab-content");
35125         container.appendChild(body);
35126         return body;
35127     },
35128     createItemBody :function(bodyEl, id){
35129         var body = Roo.getDom(id);
35130         if(!body){
35131             body = document.createElement("div");
35132             body.id = id;
35133         }
35134         //Roo.fly(body).addClass("x-tabs-item-body");
35135         Roo.fly(body).addClass("tab-pane");
35136          bodyEl.insertBefore(body, bodyEl.firstChild);
35137         return body;
35138     },
35139     /** @private */
35140     createStripElements :  function(stripEl, text, closable)
35141     {
35142         var td = document.createElement("li"); // was td..
35143         
35144         
35145         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35146         
35147         
35148         stripEl.appendChild(td);
35149         /*if(closable){
35150             td.className = "x-tabs-closable";
35151             if(!this.closeTpl){
35152                 this.closeTpl = new Roo.Template(
35153                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35154                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35155                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35156                 );
35157             }
35158             var el = this.closeTpl.overwrite(td, {"text": text});
35159             var close = el.getElementsByTagName("div")[0];
35160             var inner = el.getElementsByTagName("em")[0];
35161             return {"el": el, "close": close, "inner": inner};
35162         } else {
35163         */
35164         // not sure what this is..
35165             if(!this.tabTpl){
35166                 //this.tabTpl = new Roo.Template(
35167                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35168                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35169                 //);
35170                 this.tabTpl = new Roo.Template(
35171                    '<a href="#">' +
35172                    '<span unselectable="on"' +
35173                             (this.disableTooltips ? '' : ' title="{text}"') +
35174                             ' >{text}</span></span></a>'
35175                 );
35176                 
35177             }
35178             var el = this.tabTpl.overwrite(td, {"text": text});
35179             var inner = el.getElementsByTagName("span")[0];
35180             return {"el": el, "inner": inner};
35181         //}
35182     }
35183         
35184     
35185 });
35186
35187 /**
35188  * @class Roo.TabPanelItem
35189  * @extends Roo.util.Observable
35190  * Represents an individual item (tab plus body) in a TabPanel.
35191  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35192  * @param {String} id The id of this TabPanelItem
35193  * @param {String} text The text for the tab of this TabPanelItem
35194  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35195  */
35196 Roo.bootstrap.panel.TabItem = function(config){
35197     /**
35198      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35199      * @type Roo.TabPanel
35200      */
35201     this.tabPanel = config.panel;
35202     /**
35203      * The id for this TabPanelItem
35204      * @type String
35205      */
35206     this.id = config.id;
35207     /** @private */
35208     this.disabled = false;
35209     /** @private */
35210     this.text = config.text;
35211     /** @private */
35212     this.loaded = false;
35213     this.closable = config.closable;
35214
35215     /**
35216      * The body element for this TabPanelItem.
35217      * @type Roo.Element
35218      */
35219     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35220     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35221     this.bodyEl.setStyle("display", "block");
35222     this.bodyEl.setStyle("zoom", "1");
35223     //this.hideAction();
35224
35225     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35226     /** @private */
35227     this.el = Roo.get(els.el);
35228     this.inner = Roo.get(els.inner, true);
35229     this.textEl = Roo.get(this.el.dom.firstChild, true);
35230     this.pnode = Roo.get(els.el.parentNode, true);
35231     this.el.on("mousedown", this.onTabMouseDown, this);
35232     this.el.on("click", this.onTabClick, this);
35233     /** @private */
35234     if(config.closable){
35235         var c = Roo.get(els.close, true);
35236         c.dom.title = this.closeText;
35237         c.addClassOnOver("close-over");
35238         c.on("click", this.closeClick, this);
35239      }
35240
35241     this.addEvents({
35242          /**
35243          * @event activate
35244          * Fires when this tab becomes the active tab.
35245          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35246          * @param {Roo.TabPanelItem} this
35247          */
35248         "activate": true,
35249         /**
35250          * @event beforeclose
35251          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35252          * @param {Roo.TabPanelItem} this
35253          * @param {Object} e Set cancel to true on this object to cancel the close.
35254          */
35255         "beforeclose": true,
35256         /**
35257          * @event close
35258          * Fires when this tab is closed.
35259          * @param {Roo.TabPanelItem} this
35260          */
35261          "close": true,
35262         /**
35263          * @event deactivate
35264          * Fires when this tab is no longer the active tab.
35265          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35266          * @param {Roo.TabPanelItem} this
35267          */
35268          "deactivate" : true
35269     });
35270     this.hidden = false;
35271
35272     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35273 };
35274
35275 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35276            {
35277     purgeListeners : function(){
35278        Roo.util.Observable.prototype.purgeListeners.call(this);
35279        this.el.removeAllListeners();
35280     },
35281     /**
35282      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35283      */
35284     show : function(){
35285         this.pnode.addClass("active");
35286         this.showAction();
35287         if(Roo.isOpera){
35288             this.tabPanel.stripWrap.repaint();
35289         }
35290         this.fireEvent("activate", this.tabPanel, this);
35291     },
35292
35293     /**
35294      * Returns true if this tab is the active tab.
35295      * @return {Boolean}
35296      */
35297     isActive : function(){
35298         return this.tabPanel.getActiveTab() == this;
35299     },
35300
35301     /**
35302      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35303      */
35304     hide : function(){
35305         this.pnode.removeClass("active");
35306         this.hideAction();
35307         this.fireEvent("deactivate", this.tabPanel, this);
35308     },
35309
35310     hideAction : function(){
35311         this.bodyEl.hide();
35312         this.bodyEl.setStyle("position", "absolute");
35313         this.bodyEl.setLeft("-20000px");
35314         this.bodyEl.setTop("-20000px");
35315     },
35316
35317     showAction : function(){
35318         this.bodyEl.setStyle("position", "relative");
35319         this.bodyEl.setTop("");
35320         this.bodyEl.setLeft("");
35321         this.bodyEl.show();
35322     },
35323
35324     /**
35325      * Set the tooltip for the tab.
35326      * @param {String} tooltip The tab's tooltip
35327      */
35328     setTooltip : function(text){
35329         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35330             this.textEl.dom.qtip = text;
35331             this.textEl.dom.removeAttribute('title');
35332         }else{
35333             this.textEl.dom.title = text;
35334         }
35335     },
35336
35337     onTabClick : function(e){
35338         e.preventDefault();
35339         this.tabPanel.activate(this.id);
35340     },
35341
35342     onTabMouseDown : function(e){
35343         e.preventDefault();
35344         this.tabPanel.activate(this.id);
35345     },
35346 /*
35347     getWidth : function(){
35348         return this.inner.getWidth();
35349     },
35350
35351     setWidth : function(width){
35352         var iwidth = width - this.pnode.getPadding("lr");
35353         this.inner.setWidth(iwidth);
35354         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35355         this.pnode.setWidth(width);
35356     },
35357 */
35358     /**
35359      * Show or hide the tab
35360      * @param {Boolean} hidden True to hide or false to show.
35361      */
35362     setHidden : function(hidden){
35363         this.hidden = hidden;
35364         this.pnode.setStyle("display", hidden ? "none" : "");
35365     },
35366
35367     /**
35368      * Returns true if this tab is "hidden"
35369      * @return {Boolean}
35370      */
35371     isHidden : function(){
35372         return this.hidden;
35373     },
35374
35375     /**
35376      * Returns the text for this tab
35377      * @return {String}
35378      */
35379     getText : function(){
35380         return this.text;
35381     },
35382     /*
35383     autoSize : function(){
35384         //this.el.beginMeasure();
35385         this.textEl.setWidth(1);
35386         /*
35387          *  #2804 [new] Tabs in Roojs
35388          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35389          */
35390         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35391         //this.el.endMeasure();
35392     //},
35393
35394     /**
35395      * Sets the text for the tab (Note: this also sets the tooltip text)
35396      * @param {String} text The tab's text and tooltip
35397      */
35398     setText : function(text){
35399         this.text = text;
35400         this.textEl.update(text);
35401         this.setTooltip(text);
35402         //if(!this.tabPanel.resizeTabs){
35403         //    this.autoSize();
35404         //}
35405     },
35406     /**
35407      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35408      */
35409     activate : function(){
35410         this.tabPanel.activate(this.id);
35411     },
35412
35413     /**
35414      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35415      */
35416     disable : function(){
35417         if(this.tabPanel.active != this){
35418             this.disabled = true;
35419             this.pnode.addClass("disabled");
35420         }
35421     },
35422
35423     /**
35424      * Enables this TabPanelItem if it was previously disabled.
35425      */
35426     enable : function(){
35427         this.disabled = false;
35428         this.pnode.removeClass("disabled");
35429     },
35430
35431     /**
35432      * Sets the content for this TabPanelItem.
35433      * @param {String} content The content
35434      * @param {Boolean} loadScripts true to look for and load scripts
35435      */
35436     setContent : function(content, loadScripts){
35437         this.bodyEl.update(content, loadScripts);
35438     },
35439
35440     /**
35441      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35442      * @return {Roo.UpdateManager} The UpdateManager
35443      */
35444     getUpdateManager : function(){
35445         return this.bodyEl.getUpdateManager();
35446     },
35447
35448     /**
35449      * Set a URL to be used to load the content for this TabPanelItem.
35450      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35451      * @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)
35452      * @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)
35453      * @return {Roo.UpdateManager} The UpdateManager
35454      */
35455     setUrl : function(url, params, loadOnce){
35456         if(this.refreshDelegate){
35457             this.un('activate', this.refreshDelegate);
35458         }
35459         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35460         this.on("activate", this.refreshDelegate);
35461         return this.bodyEl.getUpdateManager();
35462     },
35463
35464     /** @private */
35465     _handleRefresh : function(url, params, loadOnce){
35466         if(!loadOnce || !this.loaded){
35467             var updater = this.bodyEl.getUpdateManager();
35468             updater.update(url, params, this._setLoaded.createDelegate(this));
35469         }
35470     },
35471
35472     /**
35473      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35474      *   Will fail silently if the setUrl method has not been called.
35475      *   This does not activate the panel, just updates its content.
35476      */
35477     refresh : function(){
35478         if(this.refreshDelegate){
35479            this.loaded = false;
35480            this.refreshDelegate();
35481         }
35482     },
35483
35484     /** @private */
35485     _setLoaded : function(){
35486         this.loaded = true;
35487     },
35488
35489     /** @private */
35490     closeClick : function(e){
35491         var o = {};
35492         e.stopEvent();
35493         this.fireEvent("beforeclose", this, o);
35494         if(o.cancel !== true){
35495             this.tabPanel.removeTab(this.id);
35496         }
35497     },
35498     /**
35499      * The text displayed in the tooltip for the close icon.
35500      * @type String
35501      */
35502     closeText : "Close this tab"
35503 });