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
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @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)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                   //  width:400,
3043                   //  height:100,
3044                     //buttonAlign:"center",
3045                     closeClick : function(){
3046                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047                             handleButton("no");
3048                         }else{
3049                             handleButton("cancel");
3050                         }
3051                     }
3052                 });
3053                 dlg.render();
3054                 dlg.on("hide", handleHide);
3055                 mask = dlg.mask;
3056                 //dlg.addKeyListener(27, handleEsc);
3057                 buttons = {};
3058                 this.buttons = buttons;
3059                 var bt = this.buttonText;
3060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3064                 //Roo.log(buttons);
3065                 bodyEl = dlg.bodyEl.createChild({
3066
3067                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068                         '<textarea class="roo-mb-textarea"></textarea>' +
3069                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3070                 });
3071                 msgEl = bodyEl.dom.firstChild;
3072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073                 textboxEl.enableDisplayMode();
3074                 textboxEl.addKeyListener([10,13], function(){
3075                     if(dlg.isVisible() && opt && opt.buttons){
3076                         if(opt.buttons.ok){
3077                             handleButton("ok");
3078                         }else if(opt.buttons.yes){
3079                             handleButton("yes");
3080                         }
3081                     }
3082                 });
3083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084                 textareaEl.enableDisplayMode();
3085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086                 progressEl.enableDisplayMode();
3087                 
3088                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089                 //var pf = progressEl.dom.firstChild;
3090                 //if (pf) {
3091                     //pp = Roo.get(pf.firstChild);
3092                     //pp.setHeight(pf.offsetHeight);
3093                 //}
3094                 
3095             }
3096             return dlg;
3097         },
3098
3099         /**
3100          * Updates the message box body text
3101          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102          * the XHTML-compliant non-breaking space character '&amp;#160;')
3103          * @return {Roo.MessageBox} This message box
3104          */
3105         updateText : function(text)
3106         {
3107             if(!dlg.isVisible() && !opt.width){
3108                 dlg.dialogEl.setWidth(this.maxWidth);
3109                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3110             }
3111             msgEl.innerHTML = text || '&#160;';
3112       
3113             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3115             var w = Math.max(
3116                     Math.min(opt.width || cw , this.maxWidth), 
3117                     Math.max(opt.minWidth || this.minWidth, bwidth)
3118             );
3119             if(opt.prompt){
3120                 activeTextEl.setWidth(w);
3121             }
3122             if(dlg.isVisible()){
3123                 dlg.fixedcenter = false;
3124             }
3125             // to big, make it scroll. = But as usual stupid IE does not support
3126             // !important..
3127             
3128             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3131             } else {
3132                 bodyEl.dom.style.height = '';
3133                 bodyEl.dom.style.overflowY = '';
3134             }
3135             if (cw > w) {
3136                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3137             } else {
3138                 bodyEl.dom.style.overflowX = '';
3139             }
3140             
3141             dlg.setContentSize(w, bodyEl.getHeight());
3142             if(dlg.isVisible()){
3143                 dlg.fixedcenter = true;
3144             }
3145             return this;
3146         },
3147
3148         /**
3149          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3150          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153          * @return {Roo.MessageBox} This message box
3154          */
3155         updateProgress : function(value, text){
3156             if(text){
3157                 this.updateText(text);
3158             }
3159             if (pp) { // weird bug on my firefox - for some reason this is not defined
3160                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161             }
3162             return this;
3163         },        
3164
3165         /**
3166          * Returns true if the message box is currently displayed
3167          * @return {Boolean} True if the message box is visible, else false
3168          */
3169         isVisible : function(){
3170             return dlg && dlg.isVisible();  
3171         },
3172
3173         /**
3174          * Hides the message box if it is displayed
3175          */
3176         hide : function(){
3177             if(this.isVisible()){
3178                 dlg.hide();
3179             }  
3180         },
3181
3182         /**
3183          * Displays a new message box, or reinitializes an existing message box, based on the config options
3184          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185          * The following config object properties are supported:
3186          * <pre>
3187 Property    Type             Description
3188 ----------  ---------------  ------------------------------------------------------------------------------------
3189 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3190                                    closes (defaults to undefined)
3191 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3194                                    progress and wait dialogs will ignore this property and always hide the
3195                                    close button as they can only be closed programmatically.
3196 cls               String           A custom CSS class to apply to the message box element
3197 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3198                                    displayed (defaults to 75)
3199 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3200                                    function will be btn (the name of the button that was clicked, if applicable,
3201                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3202                                    Progress and wait dialogs will ignore this option since they do not respond to
3203                                    user actions and can only be closed programmatically, so any required function
3204                                    should be called by the same code after it closes the dialog.
3205 icon              String           A CSS class that provides a background image to be used as an icon for
3206                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3208 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3209 modal             Boolean          False to allow user interaction with the page while the message box is
3210                                    displayed (defaults to true)
3211 msg               String           A string that will replace the existing message box body text (defaults
3212                                    to the XHTML-compliant non-breaking space character '&#160;')
3213 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3214 progress          Boolean          True to display a progress bar (defaults to false)
3215 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3218 title             String           The title text
3219 value             String           The string value to set into the active textbox element if displayed
3220 wait              Boolean          True to display a progress bar (defaults to false)
3221 width             Number           The width of the dialog in pixels
3222 </pre>
3223          *
3224          * Example usage:
3225          * <pre><code>
3226 Roo.Msg.show({
3227    title: 'Address',
3228    msg: 'Please enter your address:',
3229    width: 300,
3230    buttons: Roo.MessageBox.OKCANCEL,
3231    multiline: true,
3232    fn: saveAddress,
3233    animEl: 'addAddressBtn'
3234 });
3235 </code></pre>
3236          * @param {Object} config Configuration options
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         show : function(options)
3240         {
3241             
3242             // this causes nightmares if you show one dialog after another
3243             // especially on callbacks..
3244              
3245             if(this.isVisible()){
3246                 
3247                 this.hide();
3248                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3250                 Roo.log("New Dialog Message:" +  options.msg )
3251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253                 
3254             }
3255             var d = this.getDialog();
3256             opt = options;
3257             d.setTitle(opt.title || "&#160;");
3258             d.closeEl.setDisplayed(opt.closable !== false);
3259             activeTextEl = textboxEl;
3260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3261             if(opt.prompt){
3262                 if(opt.multiline){
3263                     textboxEl.hide();
3264                     textareaEl.show();
3265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3266                         opt.multiline : this.defaultTextHeight);
3267                     activeTextEl = textareaEl;
3268                 }else{
3269                     textboxEl.show();
3270                     textareaEl.hide();
3271                 }
3272             }else{
3273                 textboxEl.hide();
3274                 textareaEl.hide();
3275             }
3276             progressEl.setDisplayed(opt.progress === true);
3277             this.updateProgress(0);
3278             activeTextEl.dom.value = opt.value || "";
3279             if(opt.prompt){
3280                 dlg.setDefaultButton(activeTextEl);
3281             }else{
3282                 var bs = opt.buttons;
3283                 var db = null;
3284                 if(bs && bs.ok){
3285                     db = buttons["ok"];
3286                 }else if(bs && bs.yes){
3287                     db = buttons["yes"];
3288                 }
3289                 dlg.setDefaultButton(db);
3290             }
3291             bwidth = updateButtons(opt.buttons);
3292             this.updateText(opt.msg);
3293             if(opt.cls){
3294                 d.el.addClass(opt.cls);
3295             }
3296             d.proxyDrag = opt.proxyDrag === true;
3297             d.modal = opt.modal !== false;
3298             d.mask = opt.modal !== false ? mask : false;
3299             if(!d.isVisible()){
3300                 // force it to the end of the z-index stack so it gets a cursor in FF
3301                 document.body.appendChild(dlg.el.dom);
3302                 d.animateTarget = null;
3303                 d.show(options.animEl);
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311          * and closing the message box when the process is complete.
3312          * @param {String} title The title bar text
3313          * @param {String} msg The message box body text
3314          * @return {Roo.MessageBox} This message box
3315          */
3316         progress : function(title, msg){
3317             this.show({
3318                 title : title,
3319                 msg : msg,
3320                 buttons: false,
3321                 progress:true,
3322                 closable:false,
3323                 minWidth: this.minProgressWidth,
3324                 modal : true
3325             });
3326             return this;
3327         },
3328
3329         /**
3330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331          * If a callback function is passed it will be called after the user clicks the button, and the
3332          * id of the button that was clicked will be passed as the only parameter to the callback
3333          * (could also be the top-right close button).
3334          * @param {String} title The title bar text
3335          * @param {String} msg The message box body text
3336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337          * @param {Object} scope (optional) The scope of the callback function
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         alert : function(title, msg, fn, scope)
3341         {
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: this.OK,
3346                 fn: fn,
3347                 closable : false,
3348                 scope : scope,
3349                 modal : true
3350             });
3351             return this;
3352         },
3353
3354         /**
3355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357          * You are responsible for closing the message box when the process is complete.
3358          * @param {String} msg The message box body text
3359          * @param {String} title (optional) The title bar text
3360          * @return {Roo.MessageBox} This message box
3361          */
3362         wait : function(msg, title){
3363             this.show({
3364                 title : title,
3365                 msg : msg,
3366                 buttons: false,
3367                 closable:false,
3368                 progress:true,
3369                 modal:true,
3370                 width:300,
3371                 wait:true
3372             });
3373             waitTimer = Roo.TaskMgr.start({
3374                 run: function(i){
3375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3376                 },
3377                 interval: 1000
3378             });
3379             return this;
3380         },
3381
3382         /**
3383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386          * @param {String} title The title bar text
3387          * @param {String} msg The message box body text
3388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389          * @param {Object} scope (optional) The scope of the callback function
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         confirm : function(title, msg, fn, scope){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: this.YESNO,
3397                 fn: fn,
3398                 scope : scope,
3399                 modal : true
3400             });
3401             return this;
3402         },
3403
3404         /**
3405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408          * (could also be the top-right close button) and the text that was entered will be passed as the two
3409          * parameters to the callback.
3410          * @param {String} title The title bar text
3411          * @param {String} msg The message box body text
3412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413          * @param {Object} scope (optional) The scope of the callback function
3414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         prompt : function(title, msg, fn, scope, multiline){
3419             this.show({
3420                 title : title,
3421                 msg : msg,
3422                 buttons: this.OKCANCEL,
3423                 fn: fn,
3424                 minWidth:250,
3425                 scope : scope,
3426                 prompt:true,
3427                 multiline: multiline,
3428                 modal : true
3429             });
3430             return this;
3431         },
3432
3433         /**
3434          * Button config that displays a single OK button
3435          * @type Object
3436          */
3437         OK : {ok:true},
3438         /**
3439          * Button config that displays Yes and No buttons
3440          * @type Object
3441          */
3442         YESNO : {yes:true, no:true},
3443         /**
3444          * Button config that displays OK and Cancel buttons
3445          * @type Object
3446          */
3447         OKCANCEL : {ok:true, cancel:true},
3448         /**
3449          * Button config that displays Yes, No and Cancel buttons
3450          * @type Object
3451          */
3452         YESNOCANCEL : {yes:true, no:true, cancel:true},
3453
3454         /**
3455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456          * @type Number
3457          */
3458         defaultTextHeight : 75,
3459         /**
3460          * The maximum width in pixels of the message box (defaults to 600)
3461          * @type Number
3462          */
3463         maxWidth : 600,
3464         /**
3465          * The minimum width in pixels of the message box (defaults to 100)
3466          * @type Number
3467          */
3468         minWidth : 100,
3469         /**
3470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472          * @type Number
3473          */
3474         minProgressWidth : 250,
3475         /**
3476          * An object containing the default button text strings that can be overriden for localized language support.
3477          * Supported properties are: ok, cancel, yes and no.
3478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3479          * @type Object
3480          */
3481         buttonText : {
3482             ok : "OK",
3483             cancel : "Cancel",
3484             yes : "Yes",
3485             no : "No"
3486         }
3487     };
3488 }();
3489
3490 /**
3491  * Shorthand for {@link Roo.MessageBox}
3492  */
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3495 /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Navbar
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Navbar class
3506
3507  * @constructor
3508  * Create a new Navbar
3509  * @param {Object} config The config object
3510  */
3511
3512
3513 Roo.bootstrap.Navbar = function(config){
3514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3515     this.addEvents({
3516         // raw events
3517         /**
3518          * @event beforetoggle
3519          * Fire before toggle the menu
3520          * @param {Roo.EventObject} e
3521          */
3522         "beforetoggle" : true
3523     });
3524 };
3525
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3527     
3528     
3529    
3530     // private
3531     navItems : false,
3532     loadMask : false,
3533     
3534     
3535     getAutoCreate : function(){
3536         
3537         
3538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3539         
3540     },
3541     
3542     initEvents :function ()
3543     {
3544         //Roo.log(this.el.select('.navbar-toggle',true));
3545         this.el.select('.navbar-toggle',true).on('click', function() {
3546             if(this.fireEvent('beforetoggle', this) !== false){
3547                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3548             }
3549             
3550         }, this);
3551         
3552         var mark = {
3553             tag: "div",
3554             cls:"x-dlg-mask"
3555         };
3556         
3557         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3558         
3559         var size = this.el.getSize();
3560         this.maskEl.setSize(size.width, size.height);
3561         this.maskEl.enableDisplayMode("block");
3562         this.maskEl.hide();
3563         
3564         if(this.loadMask){
3565             this.maskEl.show();
3566         }
3567     },
3568     
3569     
3570     getChildContainer : function()
3571     {
3572         if (this.el.select('.collapse').getCount()) {
3573             return this.el.select('.collapse',true).first();
3574         }
3575         
3576         return this.el;
3577     },
3578     
3579     mask : function()
3580     {
3581         this.maskEl.show();
3582     },
3583     
3584     unmask : function()
3585     {
3586         this.maskEl.hide();
3587     } 
3588     
3589     
3590     
3591     
3592 });
3593
3594
3595
3596  
3597
3598  /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.NavSimplebar
3607  * @extends Roo.bootstrap.Navbar
3608  * Bootstrap Sidebar class
3609  *
3610  * @cfg {Boolean} inverse is inverted color
3611  * 
3612  * @cfg {String} type (nav | pills | tabs)
3613  * @cfg {Boolean} arrangement stacked | justified
3614  * @cfg {String} align (left | right) alignment
3615  * 
3616  * @cfg {Boolean} main (true|false) main nav bar? default false
3617  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3618  * 
3619  * @cfg {String} tag (header|footer|nav|div) default is nav 
3620
3621  * 
3622  * 
3623  * 
3624  * @constructor
3625  * Create a new Sidebar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.NavSimplebar = function(config){
3631     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3635     
3636     inverse: false,
3637     
3638     type: false,
3639     arrangement: '',
3640     align : false,
3641     
3642     
3643     
3644     main : false,
3645     
3646     
3647     tag : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         var cfg = {
3654             tag : this.tag || 'div',
3655             cls : 'navbar'
3656         };
3657           
3658         
3659         cfg.cn = [
3660             {
3661                 cls: 'nav',
3662                 tag : 'ul'
3663             }
3664         ];
3665         
3666          
3667         this.type = this.type || 'nav';
3668         if (['tabs','pills'].indexOf(this.type)!==-1) {
3669             cfg.cn[0].cls += ' nav-' + this.type
3670         
3671         
3672         } else {
3673             if (this.type!=='nav') {
3674                 Roo.log('nav type must be nav/tabs/pills')
3675             }
3676             cfg.cn[0].cls += ' navbar-nav'
3677         }
3678         
3679         
3680         
3681         
3682         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683             cfg.cn[0].cls += ' nav-' + this.arrangement;
3684         }
3685         
3686         
3687         if (this.align === 'right') {
3688             cfg.cn[0].cls += ' navbar-right';
3689         }
3690         
3691         if (this.inverse) {
3692             cfg.cls += ' navbar-inverse';
3693             
3694         }
3695         
3696         
3697         return cfg;
3698     
3699         
3700     }
3701     
3702     
3703     
3704 });
3705
3706
3707
3708  
3709
3710  
3711        /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavHeaderbar
3720  * @extends Roo.bootstrap.NavSimplebar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {String} brand what is brand
3724  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725  * @cfg {String} brand_href href of the brand
3726  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3727  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730  * 
3731  * @constructor
3732  * Create a new Sidebar
3733  * @param {Object} config The config object
3734  */
3735
3736
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3739       
3740 };
3741
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3743     
3744     position: '',
3745     brand: '',
3746     brand_href: false,
3747     srButton : true,
3748     autohide : false,
3749     desktopCenter : false,
3750    
3751     
3752     getAutoCreate : function(){
3753         
3754         var   cfg = {
3755             tag: this.nav || 'nav',
3756             cls: 'navbar',
3757             role: 'navigation',
3758             cn: []
3759         };
3760         
3761         var cn = cfg.cn;
3762         if (this.desktopCenter) {
3763             cn.push({cls : 'container', cn : []});
3764             cn = cn[0].cn;
3765         }
3766         
3767         if(this.srButton){
3768             cn.push({
3769                 tag: 'div',
3770                 cls: 'navbar-header',
3771                 cn: [
3772                     {
3773                         tag: 'button',
3774                         type: 'button',
3775                         cls: 'navbar-toggle',
3776                         'data-toggle': 'collapse',
3777                         cn: [
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'sr-only',
3781                                 html: 'Toggle navigation'
3782                             },
3783                             {
3784                                 tag: 'span',
3785                                 cls: 'icon-bar'
3786                             },
3787                             {
3788                                 tag: 'span',
3789                                 cls: 'icon-bar'
3790                             },
3791                             {
3792                                 tag: 'span',
3793                                 cls: 'icon-bar'
3794                             }
3795                         ]
3796                     }
3797                 ]
3798             });
3799         }
3800         
3801         cn.push({
3802             tag: 'div',
3803             cls: 'collapse navbar-collapse',
3804             cn : []
3805         });
3806         
3807         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3808         
3809         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810             cfg.cls += ' navbar-' + this.position;
3811             
3812             // tag can override this..
3813             
3814             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3815         }
3816         
3817         if (this.brand !== '') {
3818             cn[0].cn.push({
3819                 tag: 'a',
3820                 href: this.brand_href ? this.brand_href : '#',
3821                 cls: 'navbar-brand',
3822                 cn: [
3823                 this.brand
3824                 ]
3825             });
3826         }
3827         
3828         if(this.main){
3829             cfg.cls += ' main-nav';
3830         }
3831         
3832         
3833         return cfg;
3834
3835         
3836     },
3837     getHeaderChildContainer : function()
3838     {
3839         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840             return this.el.select('.navbar-header',true).first();
3841         }
3842         
3843         return this.getChildContainer();
3844     },
3845     
3846     
3847     initEvents : function()
3848     {
3849         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3850         
3851         if (this.autohide) {
3852             
3853             var prevScroll = 0;
3854             var ft = this.el;
3855             
3856             Roo.get(document).on('scroll',function(e) {
3857                 var ns = Roo.get(document).getScroll().top;
3858                 var os = prevScroll;
3859                 prevScroll = ns;
3860                 
3861                 if(ns > os){
3862                     ft.removeClass('slideDown');
3863                     ft.addClass('slideUp');
3864                     return;
3865                 }
3866                 ft.removeClass('slideUp');
3867                 ft.addClass('slideDown');
3868                  
3869               
3870           },this);
3871         }
3872     }    
3873     
3874 });
3875
3876
3877
3878  
3879
3880  /*
3881  * - LGPL
3882  *
3883  * navbar
3884  * 
3885  */
3886
3887 /**
3888  * @class Roo.bootstrap.NavSidebar
3889  * @extends Roo.bootstrap.Navbar
3890  * Bootstrap Sidebar class
3891  * 
3892  * @constructor
3893  * Create a new Sidebar
3894  * @param {Object} config The config object
3895  */
3896
3897
3898 Roo.bootstrap.NavSidebar = function(config){
3899     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3903     
3904     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3905     
3906     getAutoCreate : function(){
3907         
3908         
3909         return  {
3910             tag: 'div',
3911             cls: 'sidebar sidebar-nav'
3912         };
3913     
3914         
3915     }
3916     
3917     
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * nav group
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavGroup
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap NavGroup class
3936  * @cfg {String} align (left|right)
3937  * @cfg {Boolean} inverse
3938  * @cfg {String} type (nav|pills|tab) default nav
3939  * @cfg {String} navId - reference Id for navbar.
3940
3941  * 
3942  * @constructor
3943  * Create a new nav group
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.NavGroup = function(config){
3948     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949     this.navItems = [];
3950    
3951     Roo.bootstrap.NavGroup.register(this);
3952      this.addEvents({
3953         /**
3954              * @event changed
3955              * Fires when the active item changes
3956              * @param {Roo.bootstrap.NavGroup} this
3957              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3959          */
3960         'changed': true
3961      });
3962     
3963 };
3964
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3966     
3967     align: '',
3968     inverse: false,
3969     form: false,
3970     type: 'nav',
3971     navId : '',
3972     // private
3973     
3974     navItems : false, 
3975     
3976     getAutoCreate : function()
3977     {
3978         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3979         
3980         cfg = {
3981             tag : 'ul',
3982             cls: 'nav' 
3983         };
3984         
3985         if (['tabs','pills'].indexOf(this.type)!==-1) {
3986             cfg.cls += ' nav-' + this.type
3987         } else {
3988             if (this.type!=='nav') {
3989                 Roo.log('nav type must be nav/tabs/pills')
3990             }
3991             cfg.cls += ' navbar-nav'
3992         }
3993         
3994         if (this.parent().sidebar) {
3995             cfg = {
3996                 tag: 'ul',
3997                 cls: 'dashboard-menu sidebar-menu'
3998             };
3999             
4000             return cfg;
4001         }
4002         
4003         if (this.form === true) {
4004             cfg = {
4005                 tag: 'form',
4006                 cls: 'navbar-form'
4007             };
4008             
4009             if (this.align === 'right') {
4010                 cfg.cls += ' navbar-right';
4011             } else {
4012                 cfg.cls += ' navbar-left';
4013             }
4014         }
4015         
4016         if (this.align === 'right') {
4017             cfg.cls += ' navbar-right';
4018         }
4019         
4020         if (this.inverse) {
4021             cfg.cls += ' navbar-inverse';
4022             
4023         }
4024         
4025         
4026         return cfg;
4027     },
4028     /**
4029     * sets the active Navigation item
4030     * @param {Roo.bootstrap.NavItem} the new current navitem
4031     */
4032     setActiveItem : function(item)
4033     {
4034         var prev = false;
4035         Roo.each(this.navItems, function(v){
4036             if (v == item) {
4037                 return ;
4038             }
4039             if (v.isActive()) {
4040                 v.setActive(false, true);
4041                 prev = v;
4042                 
4043             }
4044             
4045         });
4046
4047         item.setActive(true, true);
4048         this.fireEvent('changed', this, item, prev);
4049         
4050         
4051     },
4052     /**
4053     * gets the active Navigation item
4054     * @return {Roo.bootstrap.NavItem} the current navitem
4055     */
4056     getActive : function()
4057     {
4058         
4059         var prev = false;
4060         Roo.each(this.navItems, function(v){
4061             
4062             if (v.isActive()) {
4063                 prev = v;
4064                 
4065             }
4066             
4067         });
4068         return prev;
4069     },
4070     
4071     indexOfNav : function()
4072     {
4073         
4074         var prev = false;
4075         Roo.each(this.navItems, function(v,i){
4076             
4077             if (v.isActive()) {
4078                 prev = i;
4079                 
4080             }
4081             
4082         });
4083         return prev;
4084     },
4085     /**
4086     * adds a Navigation item
4087     * @param {Roo.bootstrap.NavItem} the navitem to add
4088     */
4089     addItem : function(cfg)
4090     {
4091         var cn = new Roo.bootstrap.NavItem(cfg);
4092         this.register(cn);
4093         cn.parentId = this.id;
4094         cn.onRender(this.el, null);
4095         return cn;
4096     },
4097     /**
4098     * register a Navigation item
4099     * @param {Roo.bootstrap.NavItem} the navitem to add
4100     */
4101     register : function(item)
4102     {
4103         this.navItems.push( item);
4104         item.navId = this.navId;
4105     
4106     },
4107     
4108     /**
4109     * clear all the Navigation item
4110     */
4111    
4112     clearAll : function()
4113     {
4114         this.navItems = [];
4115         this.el.dom.innerHTML = '';
4116     },
4117     
4118     getNavItem: function(tabId)
4119     {
4120         var ret = false;
4121         Roo.each(this.navItems, function(e) {
4122             if (e.tabId == tabId) {
4123                ret =  e;
4124                return false;
4125             }
4126             return true;
4127             
4128         });
4129         return ret;
4130     },
4131     
4132     setActiveNext : function()
4133     {
4134         var i = this.indexOfNav(this.getActive());
4135         if (i > this.navItems.length) {
4136             return;
4137         }
4138         this.setActiveItem(this.navItems[i+1]);
4139     },
4140     setActivePrev : function()
4141     {
4142         var i = this.indexOfNav(this.getActive());
4143         if (i  < 1) {
4144             return;
4145         }
4146         this.setActiveItem(this.navItems[i-1]);
4147     },
4148     clearWasActive : function(except) {
4149         Roo.each(this.navItems, function(e) {
4150             if (e.tabId != except.tabId && e.was_active) {
4151                e.was_active = false;
4152                return false;
4153             }
4154             return true;
4155             
4156         });
4157     },
4158     getWasActive : function ()
4159     {
4160         var r = false;
4161         Roo.each(this.navItems, function(e) {
4162             if (e.was_active) {
4163                r = e;
4164                return false;
4165             }
4166             return true;
4167             
4168         });
4169         return r;
4170     }
4171     
4172     
4173 });
4174
4175  
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4177     
4178     groups: {},
4179      /**
4180     * register a Navigation Group
4181     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4182     */
4183     register : function(navgrp)
4184     {
4185         this.groups[navgrp.navId] = navgrp;
4186         
4187     },
4188     /**
4189     * fetch a Navigation Group based on the navigation ID
4190     * @param {string} the navgroup to add
4191     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4192     */
4193     get: function(navId) {
4194         if (typeof(this.groups[navId]) == 'undefined') {
4195             return false;
4196             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4197         }
4198         return this.groups[navId] ;
4199     }
4200     
4201     
4202     
4203 });
4204
4205  /*
4206  * - LGPL
4207  *
4208  * row
4209  * 
4210  */
4211
4212 /**
4213  * @class Roo.bootstrap.NavItem
4214  * @extends Roo.bootstrap.Component
4215  * Bootstrap Navbar.NavItem class
4216  * @cfg {String} href  link to
4217  * @cfg {String} html content of button
4218  * @cfg {String} badge text inside badge
4219  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220  * @cfg {String} glyphicon name of glyphicon
4221  * @cfg {String} icon name of font awesome icon
4222  * @cfg {Boolean} active Is item active
4223  * @cfg {Boolean} disabled Is item disabled
4224  
4225  * @cfg {Boolean} preventDefault (true | false) default false
4226  * @cfg {String} tabId the tab that this item activates.
4227  * @cfg {String} tagtype (a|span) render as a href or span?
4228  * @cfg {Boolean} animateRef (true|false) link to element default false  
4229   
4230  * @constructor
4231  * Create a new Navbar Item
4232  * @param {Object} config The config object
4233  */
4234 Roo.bootstrap.NavItem = function(config){
4235     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4236     this.addEvents({
4237         // raw events
4238         /**
4239          * @event click
4240          * The raw click event for the entire grid.
4241          * @param {Roo.EventObject} e
4242          */
4243         "click" : true,
4244          /**
4245             * @event changed
4246             * Fires when the active item active state changes
4247             * @param {Roo.bootstrap.NavItem} this
4248             * @param {boolean} state the new state
4249              
4250          */
4251         'changed': true,
4252         /**
4253             * @event scrollto
4254             * Fires when scroll to element
4255             * @param {Roo.bootstrap.NavItem} this
4256             * @param {Object} options
4257             * @param {Roo.EventObject} e
4258              
4259          */
4260         'scrollto': true
4261     });
4262    
4263 };
4264
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4266     
4267     href: false,
4268     html: '',
4269     badge: '',
4270     icon: false,
4271     glyphicon: false,
4272     active: false,
4273     preventDefault : false,
4274     tabId : false,
4275     tagtype : 'a',
4276     disabled : false,
4277     animateRef : false,
4278     was_active : false,
4279     
4280     getAutoCreate : function(){
4281          
4282         var cfg = {
4283             tag: 'li',
4284             cls: 'nav-item'
4285             
4286         };
4287         
4288         if (this.active) {
4289             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4290         }
4291         if (this.disabled) {
4292             cfg.cls += ' disabled';
4293         }
4294         
4295         if (this.href || this.html || this.glyphicon || this.icon) {
4296             cfg.cn = [
4297                 {
4298                     tag: this.tagtype,
4299                     href : this.href || "#",
4300                     html: this.html || ''
4301                 }
4302             ];
4303             
4304             if (this.icon) {
4305                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306             }
4307
4308             if(this.glyphicon) {
4309                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4310             }
4311             
4312             if (this.menu) {
4313                 
4314                 cfg.cn[0].html += " <span class='caret'></span>";
4315              
4316             }
4317             
4318             if (this.badge !== '') {
4319                  
4320                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4321             }
4322         }
4323         
4324         
4325         
4326         return cfg;
4327     },
4328     initEvents: function() 
4329     {
4330         if (typeof (this.menu) != 'undefined') {
4331             this.menu.parentType = this.xtype;
4332             this.menu.triggerEl = this.el;
4333             this.menu = this.addxtype(Roo.apply({}, this.menu));
4334         }
4335         
4336         this.el.select('a',true).on('click', this.onClick, this);
4337         
4338         if(this.tagtype == 'span'){
4339             this.el.select('span',true).on('click', this.onClick, this);
4340         }
4341        
4342         // at this point parent should be available..
4343         this.parent().register(this);
4344     },
4345     
4346     onClick : function(e)
4347     {
4348         if (e.getTarget('.dropdown-menu-item')) {
4349             // did you click on a menu itemm.... - then don't trigger onclick..
4350             return;
4351         }
4352         
4353         if(
4354                 this.preventDefault || 
4355                 this.href == '#' 
4356         ){
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359         }
4360         
4361         if (this.disabled) {
4362             return;
4363         }
4364         
4365         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366         if (tg && tg.transition) {
4367             Roo.log("waiting for the transitionend");
4368             return;
4369         }
4370         
4371         
4372         
4373         //Roo.log("fire event clicked");
4374         if(this.fireEvent('click', this, e) === false){
4375             return;
4376         };
4377         
4378         if(this.tagtype == 'span'){
4379             return;
4380         }
4381         
4382         //Roo.log(this.href);
4383         var ael = this.el.select('a',true).first();
4384         //Roo.log(ael);
4385         
4386         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389                 return; // ignore... - it's a 'hash' to another page.
4390             }
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393             this.scrollToElement(e);
4394         }
4395         
4396         
4397         var p =  this.parent();
4398    
4399         if (['tabs','pills'].indexOf(p.type)!==-1) {
4400             if (typeof(p.setActiveItem) !== 'undefined') {
4401                 p.setActiveItem(this);
4402             }
4403         }
4404         
4405         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407             // remove the collapsed menu expand...
4408             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4409         }
4410     },
4411     
4412     isActive: function () {
4413         return this.active
4414     },
4415     setActive : function(state, fire, is_was_active)
4416     {
4417         if (this.active && !state && this.navId) {
4418             this.was_active = true;
4419             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420             if (nv) {
4421                 nv.clearWasActive(this);
4422             }
4423             
4424         }
4425         this.active = state;
4426         
4427         if (!state ) {
4428             this.el.removeClass('active');
4429         } else if (!this.el.hasClass('active')) {
4430             this.el.addClass('active');
4431         }
4432         if (fire) {
4433             this.fireEvent('changed', this, state);
4434         }
4435         
4436         // show a panel if it's registered and related..
4437         
4438         if (!this.navId || !this.tabId || !state || is_was_active) {
4439             return;
4440         }
4441         
4442         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4443         if (!tg) {
4444             return;
4445         }
4446         var pan = tg.getPanelByName(this.tabId);
4447         if (!pan) {
4448             return;
4449         }
4450         // if we can not flip to new panel - go back to old nav highlight..
4451         if (false == tg.showPanel(pan)) {
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 var onav = nv.getWasActive();
4455                 if (onav) {
4456                     onav.setActive(true, false, true);
4457                 }
4458             }
4459             
4460         }
4461         
4462         
4463         
4464     },
4465      // this should not be here...
4466     setDisabled : function(state)
4467     {
4468         this.disabled = state;
4469         if (!state ) {
4470             this.el.removeClass('disabled');
4471         } else if (!this.el.hasClass('disabled')) {
4472             this.el.addClass('disabled');
4473         }
4474         
4475     },
4476     
4477     /**
4478      * Fetch the element to display the tooltip on.
4479      * @return {Roo.Element} defaults to this.el
4480      */
4481     tooltipEl : function()
4482     {
4483         return this.el.select('' + this.tagtype + '', true).first();
4484     },
4485     
4486     scrollToElement : function(e)
4487     {
4488         var c = document.body;
4489         
4490         /*
4491          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4492          */
4493         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494             c = document.documentElement;
4495         }
4496         
4497         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498         
4499         if(!target){
4500             return;
4501         }
4502
4503         var o = target.calcOffsetsTo(c);
4504         
4505         var options = {
4506             target : target,
4507             value : o[1]
4508         };
4509         
4510         this.fireEvent('scrollto', this, options, e);
4511         
4512         Roo.get(c).scrollTo('top', options.value, true);
4513         
4514         return;
4515     }
4516 });
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * sidebar item
4523  *
4524  *  li
4525  *    <span> icon </span>
4526  *    <span> text </span>
4527  *    <span>badge </span>
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.NavSidebarItem
4532  * @extends Roo.bootstrap.NavItem
4533  * Bootstrap Navbar.NavSidebarItem class
4534  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535  * {bool} open is the menu open
4536  * @constructor
4537  * Create a new Navbar Button
4538  * @param {Object} config The config object
4539  */
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4542     this.addEvents({
4543         // raw events
4544         /**
4545          * @event click
4546          * The raw click event for the entire grid.
4547          * @param {Roo.EventObject} e
4548          */
4549         "click" : true,
4550          /**
4551             * @event changed
4552             * Fires when the active item active state changes
4553             * @param {Roo.bootstrap.NavSidebarItem} this
4554             * @param {boolean} state the new state
4555              
4556          */
4557         'changed': true
4558     });
4559    
4560 };
4561
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4563     
4564     badgeWeight : 'default',
4565     
4566     open: false,
4567     
4568     getAutoCreate : function(){
4569         
4570         
4571         var a = {
4572                 tag: 'a',
4573                 href : this.href || '#',
4574                 cls: '',
4575                 html : '',
4576                 cn : []
4577         };
4578         var cfg = {
4579             tag: 'li',
4580             cls: '',
4581             cn: [ a ]
4582         };
4583         var span = {
4584             tag: 'span',
4585             html : this.html || ''
4586         };
4587         
4588         
4589         if (this.active) {
4590             cfg.cls += ' active';
4591         }
4592         
4593         if (this.disabled) {
4594             cfg.cls += ' disabled';
4595         }
4596         if (this.open) {
4597             cfg.cls += ' open x-open';
4598         }
4599         // left icon..
4600         if (this.glyphicon || this.icon) {
4601             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4602             a.cn.push({ tag : 'i', cls : c }) ;
4603         }
4604         // html..
4605         a.cn.push(span);
4606         // then badge..
4607         if (this.badge !== '') {
4608             
4609             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4610         }
4611         // fi
4612         if (this.menu) {
4613             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614             a.cls += 'dropdown-toggle treeview' ;
4615         }
4616         
4617         return cfg;
4618          
4619            
4620     },
4621     
4622     initEvents : function()
4623     { 
4624         if (typeof (this.menu) != 'undefined') {
4625             this.menu.parentType = this.xtype;
4626             this.menu.triggerEl = this.el;
4627             this.menu = this.addxtype(Roo.apply({}, this.menu));
4628         }
4629         
4630         this.el.on('click', this.onClick, this);
4631        
4632     
4633         if(this.badge !== ''){
4634  
4635             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4636         }
4637         
4638     },
4639     
4640     onClick : function(e)
4641     {
4642         if(this.disabled){
4643             e.preventDefault();
4644             return;
4645         }
4646         
4647         if(this.preventDefault){
4648             e.preventDefault();
4649         }
4650         
4651         this.fireEvent('click', this);
4652     },
4653     
4654     disable : function()
4655     {
4656         this.setDisabled(true);
4657     },
4658     
4659     enable : function()
4660     {
4661         this.setDisabled(false);
4662     },
4663     
4664     setDisabled : function(state)
4665     {
4666         if(this.disabled == state){
4667             return;
4668         }
4669         
4670         this.disabled = state;
4671         
4672         if (state) {
4673             this.el.addClass('disabled');
4674             return;
4675         }
4676         
4677         this.el.removeClass('disabled');
4678         
4679         return;
4680     },
4681     
4682     setActive : function(state)
4683     {
4684         if(this.active == state){
4685             return;
4686         }
4687         
4688         this.active = state;
4689         
4690         if (state) {
4691             this.el.addClass('active');
4692             return;
4693         }
4694         
4695         this.el.removeClass('active');
4696         
4697         return;
4698     },
4699     
4700     isActive: function () 
4701     {
4702         return this.active;
4703     },
4704     
4705     setBadge : function(str)
4706     {
4707         if(!this.badgeEl){
4708             return;
4709         }
4710         
4711         this.badgeEl.dom.innerHTML = str;
4712     }
4713     
4714    
4715      
4716  
4717 });
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * row
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Row
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Row class (contains columns...)
4731  * 
4732  * @constructor
4733  * Create a new Row
4734  * @param {Object} config The config object
4735  */
4736
4737 Roo.bootstrap.Row = function(config){
4738     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 };
4740
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4742     
4743     getAutoCreate : function(){
4744        return {
4745             cls: 'row clearfix'
4746        };
4747     }
4748     
4749     
4750 });
4751
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * element
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Element
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Element class
4765  * @cfg {String} html contents of the element
4766  * @cfg {String} tag tag of the element
4767  * @cfg {String} cls class of the element
4768  * @cfg {Boolean} preventDefault (true|false) default false
4769  * @cfg {Boolean} clickable (true|false) default false
4770  * 
4771  * @constructor
4772  * Create a new Element
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Element = function(config){
4777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778     
4779     this.addEvents({
4780         // raw events
4781         /**
4782          * @event click
4783          * When a element is chick
4784          * @param {Roo.bootstrap.Element} this
4785          * @param {Roo.EventObject} e
4786          */
4787         "click" : true
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4792     
4793     tag: 'div',
4794     cls: '',
4795     html: '',
4796     preventDefault: false, 
4797     clickable: false,
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: this.tag,
4803             cls: this.cls,
4804             html: this.html
4805         };
4806         
4807         return cfg;
4808     },
4809     
4810     initEvents: function() 
4811     {
4812         Roo.bootstrap.Element.superclass.initEvents.call(this);
4813         
4814         if(this.clickable){
4815             this.el.on('click', this.onClick, this);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.preventDefault){
4823             e.preventDefault();
4824         }
4825         
4826         this.fireEvent('click', this, e);
4827     },
4828     
4829     getValue : function()
4830     {
4831         return this.el.dom.innerHTML;
4832     },
4833     
4834     setValue : function(value)
4835     {
4836         this.el.dom.innerHTML = value;
4837     }
4838    
4839 });
4840
4841  
4842
4843  /*
4844  * - LGPL
4845  *
4846  * pagination
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Pagination
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Pagination class
4854  * @cfg {String} size xs | sm | md | lg
4855  * @cfg {Boolean} inverse false | true
4856  * 
4857  * @constructor
4858  * Create a new Pagination
4859  * @param {Object} config The config object
4860  */
4861
4862 Roo.bootstrap.Pagination = function(config){
4863     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 };
4865
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4867     
4868     cls: false,
4869     size: false,
4870     inverse: false,
4871     
4872     getAutoCreate : function(){
4873         var cfg = {
4874             tag: 'ul',
4875                 cls: 'pagination'
4876         };
4877         if (this.inverse) {
4878             cfg.cls += ' inverse';
4879         }
4880         if (this.html) {
4881             cfg.html=this.html;
4882         }
4883         if (this.cls) {
4884             cfg.cls += " " + this.cls;
4885         }
4886         return cfg;
4887     }
4888    
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * Pagination item
4897  * 
4898  */
4899
4900
4901 /**
4902  * @class Roo.bootstrap.PaginationItem
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap PaginationItem class
4905  * @cfg {String} html text
4906  * @cfg {String} href the link
4907  * @cfg {Boolean} preventDefault (true | false) default true
4908  * @cfg {Boolean} active (true | false) default false
4909  * @cfg {Boolean} disabled default false
4910  * 
4911  * 
4912  * @constructor
4913  * Create a new PaginationItem
4914  * @param {Object} config The config object
4915  */
4916
4917
4918 Roo.bootstrap.PaginationItem = function(config){
4919     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * The raw click event for the entire grid.
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4932     
4933     href : false,
4934     html : false,
4935     preventDefault: true,
4936     active : false,
4937     cls : false,
4938     disabled: false,
4939     
4940     getAutoCreate : function(){
4941         var cfg= {
4942             tag: 'li',
4943             cn: [
4944                 {
4945                     tag : 'a',
4946                     href : this.href ? this.href : '#',
4947                     html : this.html ? this.html : ''
4948                 }
4949             ]
4950         };
4951         
4952         if(this.cls){
4953             cfg.cls = this.cls;
4954         }
4955         
4956         if(this.disabled){
4957             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4958         }
4959         
4960         if(this.active){
4961             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962         }
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() {
4968         
4969         this.el.on('click', this.onClick, this);
4970         
4971     },
4972     onClick : function(e)
4973     {
4974         Roo.log('PaginationItem on click ');
4975         if(this.preventDefault){
4976             e.preventDefault();
4977         }
4978         
4979         if(this.disabled){
4980             return;
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * slider
4994  * 
4995  */
4996
4997
4998 /**
4999  * @class Roo.bootstrap.Slider
5000  * @extends Roo.bootstrap.Component
5001  * Bootstrap Slider class
5002  *    
5003  * @constructor
5004  * Create a new Slider
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Slider = function(config){
5009     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5013     
5014     getAutoCreate : function(){
5015         
5016         var cfg = {
5017             tag: 'div',
5018             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5019             cn: [
5020                 {
5021                     tag: 'a',
5022                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023                 }
5024             ]
5025         };
5026         
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  /*
5033  * Based on:
5034  * Ext JS Library 1.1.1
5035  * Copyright(c) 2006-2007, Ext JS, LLC.
5036  *
5037  * Originally Released Under LGPL - original licence link has changed is not relivant.
5038  *
5039  * Fork - LGPL
5040  * <script type="text/javascript">
5041  */
5042  
5043
5044 /**
5045  * @class Roo.grid.ColumnModel
5046  * @extends Roo.util.Observable
5047  * This is the default implementation of a ColumnModel used by the Grid. It defines
5048  * the columns in the grid.
5049  * <br>Usage:<br>
5050  <pre><code>
5051  var colModel = new Roo.grid.ColumnModel([
5052         {header: "Ticker", width: 60, sortable: true, locked: true},
5053         {header: "Company Name", width: 150, sortable: true},
5054         {header: "Market Cap.", width: 100, sortable: true},
5055         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056         {header: "Employees", width: 100, sortable: true, resizable: false}
5057  ]);
5058  </code></pre>
5059  * <p>
5060  
5061  * The config options listed for this class are options which may appear in each
5062  * individual column definition.
5063  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5064  * @constructor
5065  * @param {Object} config An Array of column config objects. See this class's
5066  * config objects for details.
5067 */
5068 Roo.grid.ColumnModel = function(config){
5069         /**
5070      * The config passed into the constructor
5071      */
5072     this.config = config;
5073     this.lookup = {};
5074
5075     // if no id, create one
5076     // if the column does not have a dataIndex mapping,
5077     // map it to the order it is in the config
5078     for(var i = 0, len = config.length; i < len; i++){
5079         var c = config[i];
5080         if(typeof c.dataIndex == "undefined"){
5081             c.dataIndex = i;
5082         }
5083         if(typeof c.renderer == "string"){
5084             c.renderer = Roo.util.Format[c.renderer];
5085         }
5086         if(typeof c.id == "undefined"){
5087             c.id = Roo.id();
5088         }
5089         if(c.editor && c.editor.xtype){
5090             c.editor  = Roo.factory(c.editor, Roo.grid);
5091         }
5092         if(c.editor && c.editor.isFormField){
5093             c.editor = new Roo.grid.GridEditor(c.editor);
5094         }
5095         this.lookup[c.id] = c;
5096     }
5097
5098     /**
5099      * The width of columns which have no width specified (defaults to 100)
5100      * @type Number
5101      */
5102     this.defaultWidth = 100;
5103
5104     /**
5105      * Default sortable of columns which have no sortable specified (defaults to false)
5106      * @type Boolean
5107      */
5108     this.defaultSortable = false;
5109
5110     this.addEvents({
5111         /**
5112              * @event widthchange
5113              * Fires when the width of a column changes.
5114              * @param {ColumnModel} this
5115              * @param {Number} columnIndex The column index
5116              * @param {Number} newWidth The new width
5117              */
5118             "widthchange": true,
5119         /**
5120              * @event headerchange
5121              * Fires when the text of a header changes.
5122              * @param {ColumnModel} this
5123              * @param {Number} columnIndex The column index
5124              * @param {Number} newText The new header text
5125              */
5126             "headerchange": true,
5127         /**
5128              * @event hiddenchange
5129              * Fires when a column is hidden or "unhidden".
5130              * @param {ColumnModel} this
5131              * @param {Number} columnIndex The column index
5132              * @param {Boolean} hidden true if hidden, false otherwise
5133              */
5134             "hiddenchange": true,
5135             /**
5136          * @event columnmoved
5137          * Fires when a column is moved.
5138          * @param {ColumnModel} this
5139          * @param {Number} oldIndex
5140          * @param {Number} newIndex
5141          */
5142         "columnmoved" : true,
5143         /**
5144          * @event columlockchange
5145          * Fires when a column's locked state is changed
5146          * @param {ColumnModel} this
5147          * @param {Number} colIndex
5148          * @param {Boolean} locked true if locked
5149          */
5150         "columnlockchange" : true
5151     });
5152     Roo.grid.ColumnModel.superclass.constructor.call(this);
5153 };
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5155     /**
5156      * @cfg {String} header The header text to display in the Grid view.
5157      */
5158     /**
5159      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161      * specified, the column's index is used as an index into the Record's data Array.
5162      */
5163     /**
5164      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166      */
5167     /**
5168      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169      * Defaults to the value of the {@link #defaultSortable} property.
5170      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171      */
5172     /**
5173      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5174      */
5175     /**
5176      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5177      */
5178     /**
5179      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180      */
5181     /**
5182      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183      */
5184     /**
5185      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189      */
5190        /**
5191      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5192      */
5193     /**
5194      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5195      */
5196     /**
5197      * @cfg {String} cursor (Optional)
5198      */
5199     /**
5200      * @cfg {String} tooltip (Optional)
5201      */
5202     /**
5203      * @cfg {Number} xs (Optional)
5204      */
5205     /**
5206      * @cfg {Number} sm (Optional)
5207      */
5208     /**
5209      * @cfg {Number} md (Optional)
5210      */
5211     /**
5212      * @cfg {Number} lg (Optional)
5213      */
5214     /**
5215      * Returns the id of the column at the specified index.
5216      * @param {Number} index The column index
5217      * @return {String} the id
5218      */
5219     getColumnId : function(index){
5220         return this.config[index].id;
5221     },
5222
5223     /**
5224      * Returns the column for a specified id.
5225      * @param {String} id The column id
5226      * @return {Object} the column
5227      */
5228     getColumnById : function(id){
5229         return this.lookup[id];
5230     },
5231
5232     
5233     /**
5234      * Returns the column for a specified dataIndex.
5235      * @param {String} dataIndex The column dataIndex
5236      * @return {Object|Boolean} the column or false if not found
5237      */
5238     getColumnByDataIndex: function(dataIndex){
5239         var index = this.findColumnIndex(dataIndex);
5240         return index > -1 ? this.config[index] : false;
5241     },
5242     
5243     /**
5244      * Returns the index for a specified column id.
5245      * @param {String} id The column id
5246      * @return {Number} the index, or -1 if not found
5247      */
5248     getIndexById : function(id){
5249         for(var i = 0, len = this.config.length; i < len; i++){
5250             if(this.config[i].id == id){
5251                 return i;
5252             }
5253         }
5254         return -1;
5255     },
5256     
5257     /**
5258      * Returns the index for a specified column dataIndex.
5259      * @param {String} dataIndex The column dataIndex
5260      * @return {Number} the index, or -1 if not found
5261      */
5262     
5263     findColumnIndex : function(dataIndex){
5264         for(var i = 0, len = this.config.length; i < len; i++){
5265             if(this.config[i].dataIndex == dataIndex){
5266                 return i;
5267             }
5268         }
5269         return -1;
5270     },
5271     
5272     
5273     moveColumn : function(oldIndex, newIndex){
5274         var c = this.config[oldIndex];
5275         this.config.splice(oldIndex, 1);
5276         this.config.splice(newIndex, 0, c);
5277         this.dataMap = null;
5278         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279     },
5280
5281     isLocked : function(colIndex){
5282         return this.config[colIndex].locked === true;
5283     },
5284
5285     setLocked : function(colIndex, value, suppressEvent){
5286         if(this.isLocked(colIndex) == value){
5287             return;
5288         }
5289         this.config[colIndex].locked = value;
5290         if(!suppressEvent){
5291             this.fireEvent("columnlockchange", this, colIndex, value);
5292         }
5293     },
5294
5295     getTotalLockedWidth : function(){
5296         var totalWidth = 0;
5297         for(var i = 0; i < this.config.length; i++){
5298             if(this.isLocked(i) && !this.isHidden(i)){
5299                 this.totalWidth += this.getColumnWidth(i);
5300             }
5301         }
5302         return totalWidth;
5303     },
5304
5305     getLockedCount : function(){
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             if(!this.isLocked(i)){
5308                 return i;
5309             }
5310         }
5311         
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the number of columns.
5317      * @return {Number}
5318      */
5319     getColumnCount : function(visibleOnly){
5320         if(visibleOnly === true){
5321             var c = 0;
5322             for(var i = 0, len = this.config.length; i < len; i++){
5323                 if(!this.isHidden(i)){
5324                     c++;
5325                 }
5326             }
5327             return c;
5328         }
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334      * @param {Function} fn
5335      * @param {Object} scope (optional)
5336      * @return {Array} result
5337      */
5338     getColumnsBy : function(fn, scope){
5339         var r = [];
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             var c = this.config[i];
5342             if(fn.call(scope||this, c, i) === true){
5343                 r[r.length] = c;
5344             }
5345         }
5346         return r;
5347     },
5348
5349     /**
5350      * Returns true if the specified column is sortable.
5351      * @param {Number} col The column index
5352      * @return {Boolean}
5353      */
5354     isSortable : function(col){
5355         if(typeof this.config[col].sortable == "undefined"){
5356             return this.defaultSortable;
5357         }
5358         return this.config[col].sortable;
5359     },
5360
5361     /**
5362      * Returns the rendering (formatting) function defined for the column.
5363      * @param {Number} col The column index.
5364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5365      */
5366     getRenderer : function(col){
5367         if(!this.config[col].renderer){
5368             return Roo.grid.ColumnModel.defaultRenderer;
5369         }
5370         return this.config[col].renderer;
5371     },
5372
5373     /**
5374      * Sets the rendering (formatting) function for a column.
5375      * @param {Number} col The column index
5376      * @param {Function} fn The function to use to process the cell's raw data
5377      * to return HTML markup for the grid view. The render function is called with
5378      * the following parameters:<ul>
5379      * <li>Data value.</li>
5380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381      * <li>css A CSS style string to apply to the table cell.</li>
5382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384      * <li>Row index</li>
5385      * <li>Column index</li>
5386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5387      */
5388     setRenderer : function(col, fn){
5389         this.config[col].renderer = fn;
5390     },
5391
5392     /**
5393      * Returns the width for the specified column.
5394      * @param {Number} col The column index
5395      * @return {Number}
5396      */
5397     getColumnWidth : function(col){
5398         return this.config[col].width * 1 || this.defaultWidth;
5399     },
5400
5401     /**
5402      * Sets the width for a column.
5403      * @param {Number} col The column index
5404      * @param {Number} width The new width
5405      */
5406     setColumnWidth : function(col, width, suppressEvent){
5407         this.config[col].width = width;
5408         this.totalWidth = null;
5409         if(!suppressEvent){
5410              this.fireEvent("widthchange", this, col, width);
5411         }
5412     },
5413
5414     /**
5415      * Returns the total width of all columns.
5416      * @param {Boolean} includeHidden True to include hidden column widths
5417      * @return {Number}
5418      */
5419     getTotalWidth : function(includeHidden){
5420         if(!this.totalWidth){
5421             this.totalWidth = 0;
5422             for(var i = 0, len = this.config.length; i < len; i++){
5423                 if(includeHidden || !this.isHidden(i)){
5424                     this.totalWidth += this.getColumnWidth(i);
5425                 }
5426             }
5427         }
5428         return this.totalWidth;
5429     },
5430
5431     /**
5432      * Returns the header for the specified column.
5433      * @param {Number} col The column index
5434      * @return {String}
5435      */
5436     getColumnHeader : function(col){
5437         return this.config[col].header;
5438     },
5439
5440     /**
5441      * Sets the header for a column.
5442      * @param {Number} col The column index
5443      * @param {String} header The new header
5444      */
5445     setColumnHeader : function(col, header){
5446         this.config[col].header = header;
5447         this.fireEvent("headerchange", this, col, header);
5448     },
5449
5450     /**
5451      * Returns the tooltip for the specified column.
5452      * @param {Number} col The column index
5453      * @return {String}
5454      */
5455     getColumnTooltip : function(col){
5456             return this.config[col].tooltip;
5457     },
5458     /**
5459      * Sets the tooltip for a column.
5460      * @param {Number} col The column index
5461      * @param {String} tooltip The new tooltip
5462      */
5463     setColumnTooltip : function(col, tooltip){
5464             this.config[col].tooltip = tooltip;
5465     },
5466
5467     /**
5468      * Returns the dataIndex for the specified column.
5469      * @param {Number} col The column index
5470      * @return {Number}
5471      */
5472     getDataIndex : function(col){
5473         return this.config[col].dataIndex;
5474     },
5475
5476     /**
5477      * Sets the dataIndex for a column.
5478      * @param {Number} col The column index
5479      * @param {Number} dataIndex The new dataIndex
5480      */
5481     setDataIndex : function(col, dataIndex){
5482         this.config[col].dataIndex = dataIndex;
5483     },
5484
5485     
5486     
5487     /**
5488      * Returns true if the cell is editable.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index - this is nto actually used..?
5491      * @return {Boolean}
5492      */
5493     isCellEditable : function(colIndex, rowIndex){
5494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5495     },
5496
5497     /**
5498      * Returns the editor defined for the cell/column.
5499      * return false or null to disable editing.
5500      * @param {Number} colIndex The column index
5501      * @param {Number} rowIndex The row index
5502      * @return {Object}
5503      */
5504     getCellEditor : function(colIndex, rowIndex){
5505         return this.config[colIndex].editor;
5506     },
5507
5508     /**
5509      * Sets if a column is editable.
5510      * @param {Number} col The column index
5511      * @param {Boolean} editable True if the column is editable
5512      */
5513     setEditable : function(col, editable){
5514         this.config[col].editable = editable;
5515     },
5516
5517
5518     /**
5519      * Returns true if the column is hidden.
5520      * @param {Number} colIndex The column index
5521      * @return {Boolean}
5522      */
5523     isHidden : function(colIndex){
5524         return this.config[colIndex].hidden;
5525     },
5526
5527
5528     /**
5529      * Returns true if the column width cannot be changed
5530      */
5531     isFixed : function(colIndex){
5532         return this.config[colIndex].fixed;
5533     },
5534
5535     /**
5536      * Returns true if the column can be resized
5537      * @return {Boolean}
5538      */
5539     isResizable : function(colIndex){
5540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541     },
5542     /**
5543      * Sets if a column is hidden.
5544      * @param {Number} colIndex The column index
5545      * @param {Boolean} hidden True if the column is hidden
5546      */
5547     setHidden : function(colIndex, hidden){
5548         this.config[colIndex].hidden = hidden;
5549         this.totalWidth = null;
5550         this.fireEvent("hiddenchange", this, colIndex, hidden);
5551     },
5552
5553     /**
5554      * Sets the editor for a column.
5555      * @param {Number} col The column index
5556      * @param {Object} editor The editor object
5557      */
5558     setEditor : function(col, editor){
5559         this.config[col].editor = editor;
5560     }
5561 });
5562
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5564 {
5565     if(typeof value == "object") {
5566         return value;
5567     }
5568         if(typeof value == "string" && value.length < 1){
5569             return "&#160;";
5570         }
5571     
5572         return String.format("{0}", value);
5573 };
5574
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587  
5588 /**
5589  * @class Roo.LoadMask
5590  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5591  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5593  * element's UpdateManager load indicator and will be destroyed after the initial load.
5594  * @constructor
5595  * Create a new LoadMask
5596  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597  * @param {Object} config The config object
5598  */
5599 Roo.LoadMask = function(el, config){
5600     this.el = Roo.get(el);
5601     Roo.apply(this, config);
5602     if(this.store){
5603         this.store.on('beforeload', this.onBeforeLoad, this);
5604         this.store.on('load', this.onLoad, this);
5605         this.store.on('loadexception', this.onLoadException, this);
5606         this.removeMask = false;
5607     }else{
5608         var um = this.el.getUpdateManager();
5609         um.showLoadIndicator = false; // disable the default indicator
5610         um.on('beforeupdate', this.onBeforeLoad, this);
5611         um.on('update', this.onLoad, this);
5612         um.on('failure', this.onLoad, this);
5613         this.removeMask = true;
5614     }
5615 };
5616
5617 Roo.LoadMask.prototype = {
5618     /**
5619      * @cfg {Boolean} removeMask
5620      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5622      */
5623     /**
5624      * @cfg {String} msg
5625      * The text to display in a centered loading message box (defaults to 'Loading...')
5626      */
5627     msg : 'Loading...',
5628     /**
5629      * @cfg {String} msgCls
5630      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5631      */
5632     msgCls : 'x-mask-loading',
5633
5634     /**
5635      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636      * @type Boolean
5637      */
5638     disabled: false,
5639
5640     /**
5641      * Disables the mask to prevent it from being displayed
5642      */
5643     disable : function(){
5644        this.disabled = true;
5645     },
5646
5647     /**
5648      * Enables the mask so that it can be displayed
5649      */
5650     enable : function(){
5651         this.disabled = false;
5652     },
5653     
5654     onLoadException : function()
5655     {
5656         Roo.log(arguments);
5657         
5658         if (typeof(arguments[3]) != 'undefined') {
5659             Roo.MessageBox.alert("Error loading",arguments[3]);
5660         } 
5661         /*
5662         try {
5663             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5665             }   
5666         } catch(e) {
5667             
5668         }
5669         */
5670     
5671         
5672         
5673         this.el.unmask(this.removeMask);
5674     },
5675     // private
5676     onLoad : function()
5677     {
5678         this.el.unmask(this.removeMask);
5679     },
5680
5681     // private
5682     onBeforeLoad : function(){
5683         if(!this.disabled){
5684             this.el.mask(this.msg, this.msgCls);
5685         }
5686     },
5687
5688     // private
5689     destroy : function(){
5690         if(this.store){
5691             this.store.un('beforeload', this.onBeforeLoad, this);
5692             this.store.un('load', this.onLoad, this);
5693             this.store.un('loadexception', this.onLoadException, this);
5694         }else{
5695             var um = this.el.getUpdateManager();
5696             um.un('beforeupdate', this.onBeforeLoad, this);
5697             um.un('update', this.onLoad, this);
5698             um.un('failure', this.onLoad, this);
5699         }
5700     }
5701 };/*
5702  * - LGPL
5703  *
5704  * table
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.Table
5710  * @extends Roo.bootstrap.Component
5711  * Bootstrap Table class
5712  * @cfg {String} cls table class
5713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714  * @cfg {String} bgcolor Specifies the background color for a table
5715  * @cfg {Number} border Specifies whether the table cells should have borders or not
5716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717  * @cfg {Number} cellspacing Specifies the space between cells
5718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720  * @cfg {String} sortable Specifies that the table should be sortable
5721  * @cfg {String} summary Specifies a summary of the content of a table
5722  * @cfg {Number} width Specifies the width of a table
5723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5724  * 
5725  * @cfg {boolean} striped Should the rows be alternative striped
5726  * @cfg {boolean} bordered Add borders to the table
5727  * @cfg {boolean} hover Add hover highlighting
5728  * @cfg {boolean} condensed Format condensed
5729  * @cfg {boolean} responsive Format condensed
5730  * @cfg {Boolean} loadMask (true|false) default false
5731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733  * @cfg {Boolean} rowSelection (true|false) default false
5734  * @cfg {Boolean} cellSelection (true|false) default false
5735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5737  
5738  * 
5739  * @constructor
5740  * Create a new Table
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.Table = function(config){
5745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5746     
5747   
5748     
5749     // BC...
5750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5754     
5755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5756     if (this.sm) {
5757         this.sm.grid = this;
5758         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759         this.sm = this.selModel;
5760         this.sm.xmodule = this.xmodule || false;
5761     }
5762     
5763     if (this.cm && typeof(this.cm.config) == 'undefined') {
5764         this.colModel = new Roo.grid.ColumnModel(this.cm);
5765         this.cm = this.colModel;
5766         this.cm.xmodule = this.xmodule || false;
5767     }
5768     if (this.store) {
5769         this.store= Roo.factory(this.store, Roo.data);
5770         this.ds = this.store;
5771         this.ds.xmodule = this.xmodule || false;
5772          
5773     }
5774     if (this.footer && this.store) {
5775         this.footer.dataSource = this.ds;
5776         this.footer = Roo.factory(this.footer);
5777     }
5778     
5779     /** @private */
5780     this.addEvents({
5781         /**
5782          * @event cellclick
5783          * Fires when a cell is clicked
5784          * @param {Roo.bootstrap.Table} this
5785          * @param {Roo.Element} el
5786          * @param {Number} rowIndex
5787          * @param {Number} columnIndex
5788          * @param {Roo.EventObject} e
5789          */
5790         "cellclick" : true,
5791         /**
5792          * @event celldblclick
5793          * Fires when a cell is double clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Roo.Element} el
5796          * @param {Number} rowIndex
5797          * @param {Number} columnIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "celldblclick" : true,
5801         /**
5802          * @event rowclick
5803          * Fires when a row is clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Roo.Element} el
5806          * @param {Number} rowIndex
5807          * @param {Roo.EventObject} e
5808          */
5809         "rowclick" : true,
5810         /**
5811          * @event rowdblclick
5812          * Fires when a row is double clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Roo.Element} el
5815          * @param {Number} rowIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "rowdblclick" : true,
5819         /**
5820          * @event mouseover
5821          * Fires when a mouseover occur
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "mouseover" : true,
5829         /**
5830          * @event mouseout
5831          * Fires when a mouseout occur
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "mouseout" : true,
5839         /**
5840          * @event rowclass
5841          * Fires when a row is rendered, so you can change add a style to it.
5842          * @param {Roo.bootstrap.Table} this
5843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5844          */
5845         'rowclass' : true,
5846           /**
5847          * @event rowsrendered
5848          * Fires when all the  rows have been rendered
5849          * @param {Roo.bootstrap.Table} this
5850          */
5851         'rowsrendered' : true,
5852         /**
5853          * @event contextmenu
5854          * The raw contextmenu event for the entire grid.
5855          * @param {Roo.EventObject} e
5856          */
5857         "contextmenu" : true,
5858         /**
5859          * @event rowcontextmenu
5860          * Fires when a row is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} rowIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "rowcontextmenu" : true,
5866         /**
5867          * @event cellcontextmenu
5868          * Fires when a cell is right clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Number} rowIndex
5871          * @param {Number} cellIndex
5872          * @param {Roo.EventObject} e
5873          */
5874          "cellcontextmenu" : true,
5875          /**
5876          * @event headercontextmenu
5877          * Fires when a header is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} columnIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "headercontextmenu" : true
5883     });
5884 };
5885
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5887     
5888     cls: false,
5889     align: false,
5890     bgcolor: false,
5891     border: false,
5892     cellpadding: false,
5893     cellspacing: false,
5894     frame: false,
5895     rules: false,
5896     sortable: false,
5897     summary: false,
5898     width: false,
5899     striped : false,
5900     scrollBody : false,
5901     bordered: false,
5902     hover:  false,
5903     condensed : false,
5904     responsive : false,
5905     sm : false,
5906     cm : false,
5907     store : false,
5908     loadMask : false,
5909     footerShow : true,
5910     headerShow : true,
5911   
5912     rowSelection : false,
5913     cellSelection : false,
5914     layout : false,
5915     
5916     // Roo.Element - the tbody
5917     mainBody: false,
5918     // Roo.Element - thead element
5919     mainHead: false,
5920     
5921     container: false, // used by gridpanel...
5922     
5923     getAutoCreate : function()
5924     {
5925         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5926         
5927         cfg = {
5928             tag: 'table',
5929             cls : 'table',
5930             cn : []
5931         };
5932         if (this.scrollBody) {
5933             cfg.cls += ' table-body-fixed';
5934         }    
5935         if (this.striped) {
5936             cfg.cls += ' table-striped';
5937         }
5938         
5939         if (this.hover) {
5940             cfg.cls += ' table-hover';
5941         }
5942         if (this.bordered) {
5943             cfg.cls += ' table-bordered';
5944         }
5945         if (this.condensed) {
5946             cfg.cls += ' table-condensed';
5947         }
5948         if (this.responsive) {
5949             cfg.cls += ' table-responsive';
5950         }
5951         
5952         if (this.cls) {
5953             cfg.cls+=  ' ' +this.cls;
5954         }
5955         
5956         // this lot should be simplifed...
5957         
5958         if (this.align) {
5959             cfg.align=this.align;
5960         }
5961         if (this.bgcolor) {
5962             cfg.bgcolor=this.bgcolor;
5963         }
5964         if (this.border) {
5965             cfg.border=this.border;
5966         }
5967         if (this.cellpadding) {
5968             cfg.cellpadding=this.cellpadding;
5969         }
5970         if (this.cellspacing) {
5971             cfg.cellspacing=this.cellspacing;
5972         }
5973         if (this.frame) {
5974             cfg.frame=this.frame;
5975         }
5976         if (this.rules) {
5977             cfg.rules=this.rules;
5978         }
5979         if (this.sortable) {
5980             cfg.sortable=this.sortable;
5981         }
5982         if (this.summary) {
5983             cfg.summary=this.summary;
5984         }
5985         if (this.width) {
5986             cfg.width=this.width;
5987         }
5988         if (this.layout) {
5989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990         }
5991         
5992         if(this.store || this.cm){
5993             if(this.headerShow){
5994                 cfg.cn.push(this.renderHeader());
5995             }
5996             
5997             cfg.cn.push(this.renderBody());
5998             
5999             if(this.footerShow){
6000                 cfg.cn.push(this.renderFooter());
6001             }
6002             // where does this come from?
6003             //cfg.cls+=  ' TableGrid';
6004         }
6005         
6006         return { cn : [ cfg ] };
6007     },
6008     
6009     initEvents : function()
6010     {   
6011         if(!this.store || !this.cm){
6012             return;
6013         }
6014         if (this.selModel) {
6015             this.selModel.initEvents();
6016         }
6017         
6018         
6019         //Roo.log('initEvents with ds!!!!');
6020         
6021         this.mainBody = this.el.select('tbody', true).first();
6022         this.mainHead = this.el.select('thead', true).first();
6023         
6024         
6025         
6026         
6027         var _this = this;
6028         
6029         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030             e.on('click', _this.sort, _this);
6031         });
6032         
6033         this.mainBody.on("click", this.onClick, this);
6034         this.mainBody.on("dblclick", this.onDblClick, this);
6035         
6036         // why is this done????? = it breaks dialogs??
6037         //this.parent().el.setStyle('position', 'relative');
6038         
6039         
6040         if (this.footer) {
6041             this.footer.parentId = this.id;
6042             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6043         } 
6044         
6045         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6046         
6047         this.store.on('load', this.onLoad, this);
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('update', this.onUpdate, this);
6050         this.store.on('add', this.onAdd, this);
6051         this.store.on("clear", this.clear, this);
6052         
6053         this.el.on("contextmenu", this.onContextMenu, this);
6054         
6055         this.mainBody.on('scroll', this.onBodyScroll, this);
6056         
6057         
6058     },
6059     
6060     onContextMenu : function(e, t)
6061     {
6062         this.processEvent("contextmenu", e);
6063     },
6064     
6065     processEvent : function(name, e)
6066     {
6067         if (name != 'touchstart' ) {
6068             this.fireEvent(name, e);    
6069         }
6070         
6071         var t = e.getTarget();
6072         
6073         var cell = Roo.get(t);
6074         
6075         if(!cell){
6076             return;
6077         }
6078         
6079         if(cell.findParent('tfoot', false, true)){
6080             return;
6081         }
6082         
6083         if(cell.findParent('thead', false, true)){
6084             
6085             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086                 cell = Roo.get(t).findParent('th', false, true);
6087                 if (!cell) {
6088                     Roo.log("failed to find th in thead?");
6089                     Roo.log(e.getTarget());
6090                     return;
6091                 }
6092             }
6093             
6094             var cellIndex = cell.dom.cellIndex;
6095             
6096             var ename = name == 'touchstart' ? 'click' : name;
6097             this.fireEvent("header" + ename, this, cellIndex, e);
6098             
6099             return;
6100         }
6101         
6102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103             cell = Roo.get(t).findParent('td', false, true);
6104             if (!cell) {
6105                 Roo.log("failed to find th in tbody?");
6106                 Roo.log(e.getTarget());
6107                 return;
6108             }
6109         }
6110         
6111         var row = cell.findParent('tr', false, true);
6112         var cellIndex = cell.dom.cellIndex;
6113         var rowIndex = row.dom.rowIndex - 1;
6114         
6115         if(row !== false){
6116             
6117             this.fireEvent("row" + name, this, rowIndex, e);
6118             
6119             if(cell !== false){
6120             
6121                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122             }
6123         }
6124         
6125     },
6126     
6127     onMouseover : function(e, el)
6128     {
6129         var cell = Roo.get(el);
6130         
6131         if(!cell){
6132             return;
6133         }
6134         
6135         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136             cell = cell.findParent('td', false, true);
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1; // start from 0
6142         
6143         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6144         
6145     },
6146     
6147     onMouseout : function(e, el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         var row = cell.findParent('tr', false, true);
6160         var cellIndex = cell.dom.cellIndex;
6161         var rowIndex = row.dom.rowIndex - 1; // start from 0
6162         
6163         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6164         
6165     },
6166     
6167     onClick : 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         // why??? - should these not be based on SelectionModel?
6193         if(this.cellSelection){
6194             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195         }
6196         
6197         if(this.rowSelection){
6198             this.fireEvent('rowclick', this, row, rowIndex, e);
6199         }
6200         
6201         
6202     },
6203         
6204     onDblClick : function(e,el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell || (!this.cellSelection && !this.rowSelection)){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         if(!cell || typeof(cell) == 'undefined'){
6217             return;
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         
6222         if(!row || typeof(row) == 'undefined'){
6223             return;
6224         }
6225         
6226         var cellIndex = cell.dom.cellIndex;
6227         var rowIndex = this.getRowIndex(row);
6228         
6229         if(this.cellSelection){
6230             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231         }
6232         
6233         if(this.rowSelection){
6234             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6235         }
6236     },
6237     
6238     sort : function(e,el)
6239     {
6240         var col = Roo.get(el);
6241         
6242         if(!col.hasClass('sortable')){
6243             return;
6244         }
6245         
6246         var sort = col.attr('sort');
6247         var dir = 'ASC';
6248         
6249         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6250             dir = 'DESC';
6251         }
6252         
6253         this.store.sortInfo = {field : sort, direction : dir};
6254         
6255         if (this.footer) {
6256             Roo.log("calling footer first");
6257             this.footer.onClick('first');
6258         } else {
6259         
6260             this.store.load({ params : { start : 0 } });
6261         }
6262     },
6263     
6264     renderHeader : function()
6265     {
6266         var header = {
6267             tag: 'thead',
6268             cn : []
6269         };
6270         
6271         var cm = this.cm;
6272         this.totalWidth = 0;
6273         
6274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6275             
6276             var config = cm.config[i];
6277             
6278             var c = {
6279                 tag: 'th',
6280                 style : '',
6281                 html: cm.getColumnHeader(i)
6282             };
6283             
6284             var hh = '';
6285             
6286             if(typeof(config.sortable) != 'undefined' && config.sortable){
6287                 c.cls = 'sortable';
6288                 c.html = '<i class="glyphicon"></i>' + c.html;
6289             }
6290             
6291             if(typeof(config.lgHeader) != 'undefined'){
6292                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293             }
6294             
6295             if(typeof(config.mdHeader) != 'undefined'){
6296                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297             }
6298             
6299             if(typeof(config.smHeader) != 'undefined'){
6300                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301             }
6302             
6303             if(typeof(config.xsHeader) != 'undefined'){
6304                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6305             }
6306             
6307             if(hh.length){
6308                 c.html = hh;
6309             }
6310             
6311             if(typeof(config.tooltip) != 'undefined'){
6312                 c.tooltip = config.tooltip;
6313             }
6314             
6315             if(typeof(config.colspan) != 'undefined'){
6316                 c.colspan = config.colspan;
6317             }
6318             
6319             if(typeof(config.hidden) != 'undefined' && config.hidden){
6320                 c.style += ' display:none;';
6321             }
6322             
6323             if(typeof(config.dataIndex) != 'undefined'){
6324                 c.sort = config.dataIndex;
6325             }
6326             
6327            
6328             
6329             if(typeof(config.align) != 'undefined' && config.align.length){
6330                 c.style += ' text-align:' + config.align + ';';
6331             }
6332             
6333             if(typeof(config.width) != 'undefined'){
6334                 c.style += ' width:' + config.width + 'px;';
6335                 this.totalWidth += config.width;
6336             } else {
6337                 this.totalWidth += 100; // assume minimum of 100 per column?
6338             }
6339             
6340             if(typeof(config.cls) != 'undefined'){
6341                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342             }
6343             
6344             ['xs','sm','md','lg'].map(function(size){
6345                 
6346                 if(typeof(config[size]) == 'undefined'){
6347                     return;
6348                 }
6349                 
6350                 if (!config[size]) { // 0 = hidden
6351                     c.cls += ' hidden-' + size;
6352                     return;
6353                 }
6354                 
6355                 c.cls += ' col-' + size + '-' + config[size];
6356
6357             });
6358             
6359             header.cn.push(c)
6360         }
6361         
6362         return header;
6363     },
6364     
6365     renderBody : function()
6366     {
6367         var body = {
6368             tag: 'tbody',
6369             cn : [
6370                 {
6371                     tag: 'tr',
6372                     cn : [
6373                         {
6374                             tag : 'td',
6375                             colspan :  this.cm.getColumnCount()
6376                         }
6377                     ]
6378                 }
6379             ]
6380         };
6381         
6382         return body;
6383     },
6384     
6385     renderFooter : function()
6386     {
6387         var footer = {
6388             tag: 'tfoot',
6389             cn : [
6390                 {
6391                     tag: 'tr',
6392                     cn : [
6393                         {
6394                             tag : 'td',
6395                             colspan :  this.cm.getColumnCount()
6396                         }
6397                     ]
6398                 }
6399             ]
6400         };
6401         
6402         return footer;
6403     },
6404     
6405     
6406     
6407     onLoad : function()
6408     {
6409 //        Roo.log('ds onload');
6410         this.clear();
6411         
6412         var _this = this;
6413         var cm = this.cm;
6414         var ds = this.store;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418             if (_this.store.sortInfo) {
6419                     
6420                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6422                 }
6423                 
6424                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6426                 }
6427             }
6428         });
6429         
6430         var tbody =  this.mainBody;
6431               
6432         if(ds.getCount() > 0){
6433             ds.data.each(function(d,rowIndex){
6434                 var row =  this.renderRow(cm, ds, rowIndex);
6435                 
6436                 tbody.createChild(row);
6437                 
6438                 var _this = this;
6439                 
6440                 if(row.cellObjects.length){
6441                     Roo.each(row.cellObjects, function(r){
6442                         _this.renderCellObject(r);
6443                     })
6444                 }
6445                 
6446             }, this);
6447         }
6448         
6449         Roo.each(this.el.select('tbody td', true).elements, function(e){
6450             e.on('mouseover', _this.onMouseover, _this);
6451         });
6452         
6453         Roo.each(this.el.select('tbody td', true).elements, function(e){
6454             e.on('mouseout', _this.onMouseout, _this);
6455         });
6456         this.fireEvent('rowsrendered', this);
6457         //if(this.loadMask){
6458         //    this.maskEl.hide();
6459         //}
6460         
6461         this.autoSize();
6462     },
6463     
6464     
6465     onUpdate : function(ds,record)
6466     {
6467         this.refreshRow(record);
6468         this.autoSize();
6469     },
6470     
6471     onRemove : function(ds, record, index, isUpdate){
6472         if(isUpdate !== true){
6473             this.fireEvent("beforerowremoved", this, index, record);
6474         }
6475         var bt = this.mainBody.dom;
6476         
6477         var rows = this.el.select('tbody > tr', true).elements;
6478         
6479         if(typeof(rows[index]) != 'undefined'){
6480             bt.removeChild(rows[index].dom);
6481         }
6482         
6483 //        if(bt.rows[index]){
6484 //            bt.removeChild(bt.rows[index]);
6485 //        }
6486         
6487         if(isUpdate !== true){
6488             //this.stripeRows(index);
6489             //this.syncRowHeights(index, index);
6490             //this.layout();
6491             this.fireEvent("rowremoved", this, index, record);
6492         }
6493     },
6494     
6495     onAdd : function(ds, records, rowIndex)
6496     {
6497         //Roo.log('on Add called');
6498         // - note this does not handle multiple adding very well..
6499         var bt = this.mainBody.dom;
6500         for (var i =0 ; i < records.length;i++) {
6501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502             //Roo.log(records[i]);
6503             //Roo.log(this.store.getAt(rowIndex+i));
6504             this.insertRow(this.store, rowIndex + i, false);
6505             return;
6506         }
6507         
6508     },
6509     
6510     
6511     refreshRow : function(record){
6512         var ds = this.store, index;
6513         if(typeof record == 'number'){
6514             index = record;
6515             record = ds.getAt(index);
6516         }else{
6517             index = ds.indexOf(record);
6518         }
6519         this.insertRow(ds, index, true);
6520         this.autoSize();
6521         this.onRemove(ds, record, index+1, true);
6522         this.autoSize();
6523         //this.syncRowHeights(index, index);
6524         //this.layout();
6525         this.fireEvent("rowupdated", this, index, record);
6526     },
6527     
6528     insertRow : function(dm, rowIndex, isUpdate){
6529         
6530         if(!isUpdate){
6531             this.fireEvent("beforerowsinserted", this, rowIndex);
6532         }
6533             //var s = this.getScrollState();
6534         var row = this.renderRow(this.cm, this.store, rowIndex);
6535         // insert before rowIndex..
6536         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6537         
6538         var _this = this;
6539                 
6540         if(row.cellObjects.length){
6541             Roo.each(row.cellObjects, function(r){
6542                 _this.renderCellObject(r);
6543             })
6544         }
6545             
6546         if(!isUpdate){
6547             this.fireEvent("rowsinserted", this, rowIndex);
6548             //this.syncRowHeights(firstRow, lastRow);
6549             //this.stripeRows(firstRow);
6550             //this.layout();
6551         }
6552         
6553     },
6554     
6555     
6556     getRowDom : function(rowIndex)
6557     {
6558         var rows = this.el.select('tbody > tr', true).elements;
6559         
6560         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561         
6562     },
6563     // returns the object tree for a tr..
6564   
6565     
6566     renderRow : function(cm, ds, rowIndex) 
6567     {
6568         
6569         var d = ds.getAt(rowIndex);
6570         
6571         var row = {
6572             tag : 'tr',
6573             cn : []
6574         };
6575             
6576         var cellObjects = [];
6577         
6578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579             var config = cm.config[i];
6580             
6581             var renderer = cm.getRenderer(i);
6582             var value = '';
6583             var id = false;
6584             
6585             if(typeof(renderer) !== 'undefined'){
6586                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6587             }
6588             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589             // and are rendered into the cells after the row is rendered - using the id for the element.
6590             
6591             if(typeof(value) === 'object'){
6592                 id = Roo.id();
6593                 cellObjects.push({
6594                     container : id,
6595                     cfg : value 
6596                 })
6597             }
6598             
6599             var rowcfg = {
6600                 record: d,
6601                 rowIndex : rowIndex,
6602                 colIndex : i,
6603                 rowClass : ''
6604             };
6605
6606             this.fireEvent('rowclass', this, rowcfg);
6607             
6608             var td = {
6609                 tag: 'td',
6610                 cls : rowcfg.rowClass,
6611                 style: '',
6612                 html: (typeof(value) === 'object') ? '' : value
6613             };
6614             
6615             if (id) {
6616                 td.id = id;
6617             }
6618             
6619             if(typeof(config.colspan) != 'undefined'){
6620                 td.colspan = config.colspan;
6621             }
6622             
6623             if(typeof(config.hidden) != 'undefined' && config.hidden){
6624                 td.style += ' display:none;';
6625             }
6626             
6627             if(typeof(config.align) != 'undefined' && config.align.length){
6628                 td.style += ' text-align:' + config.align + ';';
6629             }
6630             
6631             if(typeof(config.width) != 'undefined'){
6632                 td.style += ' width:' +  config.width + 'px;';
6633             }
6634             
6635             if(typeof(config.cursor) != 'undefined'){
6636                 td.style += ' cursor:' +  config.cursor + ';';
6637             }
6638             
6639             if(typeof(config.cls) != 'undefined'){
6640                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641             }
6642             
6643             ['xs','sm','md','lg'].map(function(size){
6644                 
6645                 if(typeof(config[size]) == 'undefined'){
6646                     return;
6647                 }
6648                 
6649                 if (!config[size]) { // 0 = hidden
6650                     td.cls += ' hidden-' + size;
6651                     return;
6652                 }
6653                 
6654                 td.cls += ' col-' + size + '-' + config[size];
6655
6656             });
6657              
6658             row.cn.push(td);
6659            
6660         }
6661         
6662         row.cellObjects = cellObjects;
6663         
6664         return row;
6665           
6666     },
6667     
6668     
6669     
6670     onBeforeLoad : function()
6671     {
6672         //Roo.log('ds onBeforeLoad');
6673         
6674         //this.clear();
6675         
6676         //if(this.loadMask){
6677         //    this.maskEl.show();
6678         //}
6679     },
6680      /**
6681      * Remove all rows
6682      */
6683     clear : function()
6684     {
6685         this.el.select('tbody', true).first().dom.innerHTML = '';
6686     },
6687     /**
6688      * Show or hide a row.
6689      * @param {Number} rowIndex to show or hide
6690      * @param {Boolean} state hide
6691      */
6692     setRowVisibility : function(rowIndex, state)
6693     {
6694         var bt = this.mainBody.dom;
6695         
6696         var rows = this.el.select('tbody > tr', true).elements;
6697         
6698         if(typeof(rows[rowIndex]) == 'undefined'){
6699             return;
6700         }
6701         rows[rowIndex].dom.style.display = state ? '' : 'none';
6702     },
6703     
6704     
6705     getSelectionModel : function(){
6706         if(!this.selModel){
6707             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6708         }
6709         return this.selModel;
6710     },
6711     /*
6712      * Render the Roo.bootstrap object from renderder
6713      */
6714     renderCellObject : function(r)
6715     {
6716         var _this = this;
6717         
6718         var t = r.cfg.render(r.container);
6719         
6720         if(r.cfg.cn){
6721             Roo.each(r.cfg.cn, function(c){
6722                 var child = {
6723                     container: t.getChildContainer(),
6724                     cfg: c
6725                 };
6726                 _this.renderCellObject(child);
6727             })
6728         }
6729     },
6730     
6731     getRowIndex : function(row)
6732     {
6733         var rowIndex = -1;
6734         
6735         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6736             if(el != row){
6737                 return;
6738             }
6739             
6740             rowIndex = index;
6741         });
6742         
6743         return rowIndex;
6744     },
6745      /**
6746      * Returns the grid's underlying element = used by panel.Grid
6747      * @return {Element} The element
6748      */
6749     getGridEl : function(){
6750         return this.el;
6751     },
6752      /**
6753      * Forces a resize - used by panel.Grid
6754      * @return {Element} The element
6755      */
6756     autoSize : function()
6757     {
6758         //var ctr = Roo.get(this.container.dom.parentElement);
6759         var ctr = Roo.get(this.el.dom);
6760         
6761         var thd = this.getGridEl().select('thead',true).first();
6762         var tbd = this.getGridEl().select('tbody', true).first();
6763         var tfd = this.getGridEl().select('tfoot', true).first();
6764         
6765         var cw = ctr.getWidth();
6766         
6767         if (tbd) {
6768             
6769             tbd.setSize(ctr.getWidth(),
6770                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6771             );
6772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773             cw -= barsize;
6774         }
6775         cw = Math.max(cw, this.totalWidth);
6776         this.getGridEl().select('tr',true).setWidth(cw);
6777         // resize 'expandable coloumn?
6778         
6779         return; // we doe not have a view in this design..
6780         
6781     },
6782     onBodyScroll: function()
6783     {
6784         
6785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786         this.mainHead.setStyle({
6787                     'position' : 'relative',
6788                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6789         });
6790         
6791         
6792     }
6793 });
6794
6795  
6796
6797  /*
6798  * - LGPL
6799  *
6800  * table cell
6801  * 
6802  */
6803
6804 /**
6805  * @class Roo.bootstrap.TableCell
6806  * @extends Roo.bootstrap.Component
6807  * Bootstrap TableCell class
6808  * @cfg {String} html cell contain text
6809  * @cfg {String} cls cell class
6810  * @cfg {String} tag cell tag (td|th) default td
6811  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812  * @cfg {String} align Aligns the content in a cell
6813  * @cfg {String} axis Categorizes cells
6814  * @cfg {String} bgcolor Specifies the background color of a cell
6815  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816  * @cfg {Number} colspan Specifies the number of columns a cell should span
6817  * @cfg {String} headers Specifies one or more header cells a cell is related to
6818  * @cfg {Number} height Sets the height of a cell
6819  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820  * @cfg {Number} rowspan Sets the number of rows a cell should span
6821  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822  * @cfg {String} valign Vertical aligns the content in a cell
6823  * @cfg {Number} width Specifies the width of a cell
6824  * 
6825  * @constructor
6826  * Create a new TableCell
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.TableCell = function(config){
6831     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6835     
6836     html: false,
6837     cls: false,
6838     tag: false,
6839     abbr: false,
6840     align: false,
6841     axis: false,
6842     bgcolor: false,
6843     charoff: false,
6844     colspan: false,
6845     headers: false,
6846     height: false,
6847     nowrap: false,
6848     rowspan: false,
6849     scope: false,
6850     valign: false,
6851     width: false,
6852     
6853     
6854     getAutoCreate : function(){
6855         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'td'
6859         };
6860         
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if (this.html) {
6866             cfg.html=this.html
6867         }
6868         if (this.cls) {
6869             cfg.cls=this.cls
6870         }
6871         if (this.abbr) {
6872             cfg.abbr=this.abbr
6873         }
6874         if (this.align) {
6875             cfg.align=this.align
6876         }
6877         if (this.axis) {
6878             cfg.axis=this.axis
6879         }
6880         if (this.bgcolor) {
6881             cfg.bgcolor=this.bgcolor
6882         }
6883         if (this.charoff) {
6884             cfg.charoff=this.charoff
6885         }
6886         if (this.colspan) {
6887             cfg.colspan=this.colspan
6888         }
6889         if (this.headers) {
6890             cfg.headers=this.headers
6891         }
6892         if (this.height) {
6893             cfg.height=this.height
6894         }
6895         if (this.nowrap) {
6896             cfg.nowrap=this.nowrap
6897         }
6898         if (this.rowspan) {
6899             cfg.rowspan=this.rowspan
6900         }
6901         if (this.scope) {
6902             cfg.scope=this.scope
6903         }
6904         if (this.valign) {
6905             cfg.valign=this.valign
6906         }
6907         if (this.width) {
6908             cfg.width=this.width
6909         }
6910         
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table row
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableRow
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableRow class
6930  * @cfg {String} cls row class
6931  * @cfg {String} align Aligns the content in a table row
6932  * @cfg {String} bgcolor Specifies a background color for a table row
6933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content in a table row
6935  * 
6936  * @constructor
6937  * Create a new TableRow
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableRow = function(config){
6942     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     align: false,
6949     bgcolor: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tr'
6958         };
6959             
6960         if(this.cls){
6961             cfg.cls = this.cls;
6962         }
6963         if(this.align){
6964             cfg.align = this.align;
6965         }
6966         if(this.bgcolor){
6967             cfg.bgcolor = this.bgcolor;
6968         }
6969         if(this.charoff){
6970             cfg.charoff = this.charoff;
6971         }
6972         if(this.valign){
6973             cfg.valign = this.valign;
6974         }
6975         
6976         return cfg;
6977     }
6978    
6979 });
6980
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  * table body
6987  * 
6988  */
6989
6990 /**
6991  * @class Roo.bootstrap.TableBody
6992  * @extends Roo.bootstrap.Component
6993  * Bootstrap TableBody class
6994  * @cfg {String} cls element class
6995  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996  * @cfg {String} align Aligns the content inside the element
6997  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999  * 
7000  * @constructor
7001  * Create a new TableBody
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableBody = function(config){
7006     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7010     
7011     cls: false,
7012     tag: false,
7013     align: false,
7014     charoff: false,
7015     valign: false,
7016     
7017     getAutoCreate : function(){
7018         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag: 'tbody'
7022         };
7023             
7024         if (this.cls) {
7025             cfg.cls=this.cls
7026         }
7027         if(this.tag){
7028             cfg.tag = this.tag;
7029         }
7030         
7031         if(this.align){
7032             cfg.align = this.align;
7033         }
7034         if(this.charoff){
7035             cfg.charoff = this.charoff;
7036         }
7037         if(this.valign){
7038             cfg.valign = this.valign;
7039         }
7040         
7041         return cfg;
7042     }
7043     
7044     
7045 //    initEvents : function()
7046 //    {
7047 //        
7048 //        if(!this.store){
7049 //            return;
7050 //        }
7051 //        
7052 //        this.store = Roo.factory(this.store, Roo.data);
7053 //        this.store.on('load', this.onLoad, this);
7054 //        
7055 //        this.store.load();
7056 //        
7057 //    },
7058 //    
7059 //    onLoad: function () 
7060 //    {   
7061 //        this.fireEvent('load', this);
7062 //    }
7063 //    
7064 //   
7065 });
7066
7067  
7068
7069  /*
7070  * Based on:
7071  * Ext JS Library 1.1.1
7072  * Copyright(c) 2006-2007, Ext JS, LLC.
7073  *
7074  * Originally Released Under LGPL - original licence link has changed is not relivant.
7075  *
7076  * Fork - LGPL
7077  * <script type="text/javascript">
7078  */
7079
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7082  /**
7083  * @class Roo.form.Action
7084  * Internal Class used to handle form actions
7085  * @constructor
7086  * @param {Roo.form.BasicForm} el The form element or its id
7087  * @param {Object} config Configuration options
7088  */
7089
7090  
7091  
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7094     this.form = form;
7095     this.options = options || {};
7096 };
7097 /**
7098  * Client Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7102 /**
7103  * Server Validation Failed
7104  * @const 
7105  */
7106 Roo.form.Action.SERVER_INVALID = 'server';
7107  /**
7108  * Connect to Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7112 /**
7113  * Reading Data from Server Failed
7114  * @const 
7115  */
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7117
7118 Roo.form.Action.prototype = {
7119     type : 'default',
7120     failureType : undefined,
7121     response : undefined,
7122     result : undefined,
7123
7124     // interface method
7125     run : function(options){
7126
7127     },
7128
7129     // interface method
7130     success : function(response){
7131
7132     },
7133
7134     // interface method
7135     handleResponse : function(response){
7136
7137     },
7138
7139     // default connection failure
7140     failure : function(response){
7141         
7142         this.response = response;
7143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144         this.form.afterAction(this, false);
7145     },
7146
7147     processResponse : function(response){
7148         this.response = response;
7149         if(!response.responseText){
7150             return true;
7151         }
7152         this.result = this.handleResponse(response);
7153         return this.result;
7154     },
7155
7156     // utility functions used internally
7157     getUrl : function(appendParams){
7158         var url = this.options.url || this.form.url || this.form.el.dom.action;
7159         if(appendParams){
7160             var p = this.getParams();
7161             if(p){
7162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163             }
7164         }
7165         return url;
7166     },
7167
7168     getMethod : function(){
7169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170     },
7171
7172     getParams : function(){
7173         var bp = this.form.baseParams;
7174         var p = this.options.params;
7175         if(p){
7176             if(typeof p == "object"){
7177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178             }else if(typeof p == 'string' && bp){
7179                 p += '&' + Roo.urlEncode(bp);
7180             }
7181         }else if(bp){
7182             p = Roo.urlEncode(bp);
7183         }
7184         return p;
7185     },
7186
7187     createCallback : function(){
7188         return {
7189             success: this.success,
7190             failure: this.failure,
7191             scope: this,
7192             timeout: (this.form.timeout*1000),
7193             upload: this.form.fileUpload ? this.success : undefined
7194         };
7195     }
7196 };
7197
7198 Roo.form.Action.Submit = function(form, options){
7199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 };
7201
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203     type : 'submit',
7204
7205     haveProgress : false,
7206     uploadComplete : false,
7207     
7208     // uploadProgress indicator.
7209     uploadProgress : function()
7210     {
7211         if (!this.form.progressUrl) {
7212             return;
7213         }
7214         
7215         if (!this.haveProgress) {
7216             Roo.MessageBox.progress("Uploading", "Uploading");
7217         }
7218         if (this.uploadComplete) {
7219            Roo.MessageBox.hide();
7220            return;
7221         }
7222         
7223         this.haveProgress = true;
7224    
7225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7226         
7227         var c = new Roo.data.Connection();
7228         c.request({
7229             url : this.form.progressUrl,
7230             params: {
7231                 id : uid
7232             },
7233             method: 'GET',
7234             success : function(req){
7235                //console.log(data);
7236                 var rdata = false;
7237                 var edata;
7238                 try  {
7239                    rdata = Roo.decode(req.responseText)
7240                 } catch (e) {
7241                     Roo.log("Invalid data from server..");
7242                     Roo.log(edata);
7243                     return;
7244                 }
7245                 if (!rdata || !rdata.success) {
7246                     Roo.log(rdata);
7247                     Roo.MessageBox.alert(Roo.encode(rdata));
7248                     return;
7249                 }
7250                 var data = rdata.data;
7251                 
7252                 if (this.uploadComplete) {
7253                    Roo.MessageBox.hide();
7254                    return;
7255                 }
7256                    
7257                 if (data){
7258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260                     );
7261                 }
7262                 this.uploadProgress.defer(2000,this);
7263             },
7264        
7265             failure: function(data) {
7266                 Roo.log('progress url failed ');
7267                 Roo.log(data);
7268             },
7269             scope : this
7270         });
7271            
7272     },
7273     
7274     
7275     run : function()
7276     {
7277         // run get Values on the form, so it syncs any secondary forms.
7278         this.form.getValues();
7279         
7280         var o = this.options;
7281         var method = this.getMethod();
7282         var isPost = method == 'POST';
7283         if(o.clientValidation === false || this.form.isValid()){
7284             
7285             if (this.form.progressUrl) {
7286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287                     (new Date() * 1) + '' + Math.random());
7288                     
7289             } 
7290             
7291             
7292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293                 form:this.form.el.dom,
7294                 url:this.getUrl(!isPost),
7295                 method: method,
7296                 params:isPost ? this.getParams() : null,
7297                 isUpload: this.form.fileUpload
7298             }));
7299             
7300             this.uploadProgress();
7301
7302         }else if (o.clientValidation !== false){ // client validation failed
7303             this.failureType = Roo.form.Action.CLIENT_INVALID;
7304             this.form.afterAction(this, false);
7305         }
7306     },
7307
7308     success : function(response)
7309     {
7310         this.uploadComplete= true;
7311         if (this.haveProgress) {
7312             Roo.MessageBox.hide();
7313         }
7314         
7315         
7316         var result = this.processResponse(response);
7317         if(result === true || result.success){
7318             this.form.afterAction(this, true);
7319             return;
7320         }
7321         if(result.errors){
7322             this.form.markInvalid(result.errors);
7323             this.failureType = Roo.form.Action.SERVER_INVALID;
7324         }
7325         this.form.afterAction(this, false);
7326     },
7327     failure : function(response)
7328     {
7329         this.uploadComplete= true;
7330         if (this.haveProgress) {
7331             Roo.MessageBox.hide();
7332         }
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338     
7339     handleResponse : function(response){
7340         if(this.form.errorReader){
7341             var rs = this.form.errorReader.read(response);
7342             var errors = [];
7343             if(rs.records){
7344                 for(var i = 0, len = rs.records.length; i < len; i++) {
7345                     var r = rs.records[i];
7346                     errors[i] = r.data;
7347                 }
7348             }
7349             if(errors.length < 1){
7350                 errors = null;
7351             }
7352             return {
7353                 success : rs.success,
7354                 errors : errors
7355             };
7356         }
7357         var ret = false;
7358         try {
7359             ret = Roo.decode(response.responseText);
7360         } catch (e) {
7361             ret = {
7362                 success: false,
7363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7364                 errors : []
7365             };
7366         }
7367         return ret;
7368         
7369     }
7370 });
7371
7372
7373 Roo.form.Action.Load = function(form, options){
7374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375     this.reader = this.form.reader;
7376 };
7377
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7379     type : 'load',
7380
7381     run : function(){
7382         
7383         Roo.Ajax.request(Roo.apply(
7384                 this.createCallback(), {
7385                     method:this.getMethod(),
7386                     url:this.getUrl(false),
7387                     params:this.getParams()
7388         }));
7389     },
7390
7391     success : function(response){
7392         
7393         var result = this.processResponse(response);
7394         if(result === true || !result.success || !result.data){
7395             this.failureType = Roo.form.Action.LOAD_FAILURE;
7396             this.form.afterAction(this, false);
7397             return;
7398         }
7399         this.form.clearInvalid();
7400         this.form.setValues(result.data);
7401         this.form.afterAction(this, true);
7402     },
7403
7404     handleResponse : function(response){
7405         if(this.form.reader){
7406             var rs = this.form.reader.read(response);
7407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7408             return {
7409                 success : rs.success,
7410                 data : data
7411             };
7412         }
7413         return Roo.decode(response.responseText);
7414     }
7415 });
7416
7417 Roo.form.Action.ACTION_TYPES = {
7418     'load' : Roo.form.Action.Load,
7419     'submit' : Roo.form.Action.Submit
7420 };/*
7421  * - LGPL
7422  *
7423  * form
7424  *
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.Form
7429  * @extends Roo.bootstrap.Component
7430  * Bootstrap Form class
7431  * @cfg {String} method  GET | POST (default POST)
7432  * @cfg {String} labelAlign top | left (default top)
7433  * @cfg {String} align left  | right - for navbars
7434  * @cfg {Boolean} loadMask load mask when submit (default true)
7435
7436  *
7437  * @constructor
7438  * Create a new Form
7439  * @param {Object} config The config object
7440  */
7441
7442
7443 Roo.bootstrap.Form = function(config){
7444     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445     this.addEvents({
7446         /**
7447          * @event clientvalidation
7448          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449          * @param {Form} this
7450          * @param {Boolean} valid true if the form has passed client-side validation
7451          */
7452         clientvalidation: true,
7453         /**
7454          * @event beforeaction
7455          * Fires before any action is performed. Return false to cancel the action.
7456          * @param {Form} this
7457          * @param {Action} action The action to be performed
7458          */
7459         beforeaction: true,
7460         /**
7461          * @event actionfailed
7462          * Fires when an action fails.
7463          * @param {Form} this
7464          * @param {Action} action The action that failed
7465          */
7466         actionfailed : true,
7467         /**
7468          * @event actioncomplete
7469          * Fires when an action is completed.
7470          * @param {Form} this
7471          * @param {Action} action The action that completed
7472          */
7473         actioncomplete : true
7474     });
7475
7476 };
7477
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7479
7480      /**
7481      * @cfg {String} method
7482      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7483      */
7484     method : 'POST',
7485     /**
7486      * @cfg {String} url
7487      * The URL to use for form actions if one isn't supplied in the action options.
7488      */
7489     /**
7490      * @cfg {Boolean} fileUpload
7491      * Set to true if this form is a file upload.
7492      */
7493
7494     /**
7495      * @cfg {Object} baseParams
7496      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7497      */
7498
7499     /**
7500      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7501      */
7502     timeout: 30,
7503     /**
7504      * @cfg {Sting} align (left|right) for navbar forms
7505      */
7506     align : 'left',
7507
7508     // private
7509     activeAction : null,
7510
7511     /**
7512      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513      * element by passing it or its id or mask the form itself by passing in true.
7514      * @type Mixed
7515      */
7516     waitMsgTarget : false,
7517
7518     loadMask : true,
7519     
7520     /**
7521      * @cfg {Boolean} errPopover (true|false) default false
7522      */
7523     errPopover : false,
7524
7525     getAutoCreate : function(){
7526
7527         var cfg = {
7528             tag: 'form',
7529             method : this.method || 'POST',
7530             id : this.id || Roo.id(),
7531             cls : ''
7532         };
7533         if (this.parent().xtype.match(/^Nav/)) {
7534             cfg.cls = 'navbar-form navbar-' + this.align;
7535
7536         }
7537
7538         if (this.labelAlign == 'left' ) {
7539             cfg.cls += ' form-horizontal';
7540         }
7541
7542
7543         return cfg;
7544     },
7545     initEvents : function()
7546     {
7547         this.el.on('submit', this.onSubmit, this);
7548         // this was added as random key presses on the form where triggering form submit.
7549         this.el.on('keypress', function(e) {
7550             if (e.getCharCode() != 13) {
7551                 return true;
7552             }
7553             // we might need to allow it for textareas.. and some other items.
7554             // check e.getTarget().
7555
7556             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7557                 return true;
7558             }
7559
7560             Roo.log("keypress blocked");
7561
7562             e.preventDefault();
7563             return false;
7564         });
7565         
7566     },
7567     // private
7568     onSubmit : function(e){
7569         e.stopEvent();
7570     },
7571
7572      /**
7573      * Returns true if client-side validation on the form is successful.
7574      * @return Boolean
7575      */
7576     isValid : function(){
7577         var items = this.getItems();
7578         var valid = true;
7579         var target = false;
7580         var radioSet = [];
7581         
7582         items.each(function(f){
7583             
7584             if(f.validate()){
7585                 return;
7586             }
7587             
7588             valid = false;
7589
7590             if(!target){
7591                 target = f;
7592             }
7593            
7594         });
7595         
7596         if(this.errPopover && !valid){
7597             this.showErrPopover(target);
7598         }
7599         
7600         return valid;
7601     },
7602     
7603     showErrPopover : function(target)
7604     {
7605         if(!this.errPopover){
7606             return;
7607         }
7608         
7609         target.inputEl().focus();
7610         
7611         var oIndex = target.el.getStyle('z-index');
7612         
7613         target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7614         
7615         target.el.addClass('roo-invalid-outline');
7616         
7617         var fadeout = function(){
7618             
7619             target.inputEl().un('blur', fadeout);
7620             target.inputEl().un('keyup', fadeout);
7621             
7622             target.el.setStyle('z-index', oIndex);
7623         
7624             target.el.removeClass('roo-invalid-outline');
7625             
7626         }
7627         
7628         target.inputEl().on('blur', fadeout);
7629         target.inputEl().on('keyup', fadeout);
7630         
7631         Roo.log(target.el);
7632         
7633         Roo.log(target);
7634           
7635     },
7636     
7637     /**
7638      * Returns true if any fields in this form have changed since their original load.
7639      * @return Boolean
7640      */
7641     isDirty : function(){
7642         var dirty = false;
7643         var items = this.getItems();
7644         items.each(function(f){
7645            if(f.isDirty()){
7646                dirty = true;
7647                return false;
7648            }
7649            return true;
7650         });
7651         return dirty;
7652     },
7653      /**
7654      * Performs a predefined action (submit or load) or custom actions you define on this form.
7655      * @param {String} actionName The name of the action type
7656      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7657      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7658      * accept other config options):
7659      * <pre>
7660 Property          Type             Description
7661 ----------------  ---------------  ----------------------------------------------------------------------------------
7662 url               String           The url for the action (defaults to the form's url)
7663 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7664 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7665 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7666                                    validate the form on the client (defaults to false)
7667      * </pre>
7668      * @return {BasicForm} this
7669      */
7670     doAction : function(action, options){
7671         if(typeof action == 'string'){
7672             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7673         }
7674         if(this.fireEvent('beforeaction', this, action) !== false){
7675             this.beforeAction(action);
7676             action.run.defer(100, action);
7677         }
7678         return this;
7679     },
7680
7681     // private
7682     beforeAction : function(action){
7683         var o = action.options;
7684
7685         if(this.loadMask){
7686             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7687         }
7688         // not really supported yet.. ??
7689
7690         //if(this.waitMsgTarget === true){
7691         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7692         //}else if(this.waitMsgTarget){
7693         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7694         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7695         //}else {
7696         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7697        // }
7698
7699     },
7700
7701     // private
7702     afterAction : function(action, success){
7703         this.activeAction = null;
7704         var o = action.options;
7705
7706         //if(this.waitMsgTarget === true){
7707             this.el.unmask();
7708         //}else if(this.waitMsgTarget){
7709         //    this.waitMsgTarget.unmask();
7710         //}else{
7711         //    Roo.MessageBox.updateProgress(1);
7712         //    Roo.MessageBox.hide();
7713        // }
7714         //
7715         if(success){
7716             if(o.reset){
7717                 this.reset();
7718             }
7719             Roo.callback(o.success, o.scope, [this, action]);
7720             this.fireEvent('actioncomplete', this, action);
7721
7722         }else{
7723
7724             // failure condition..
7725             // we have a scenario where updates need confirming.
7726             // eg. if a locking scenario exists..
7727             // we look for { errors : { needs_confirm : true }} in the response.
7728             if (
7729                 (typeof(action.result) != 'undefined')  &&
7730                 (typeof(action.result.errors) != 'undefined')  &&
7731                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7732            ){
7733                 var _t = this;
7734                 Roo.log("not supported yet");
7735                  /*
7736
7737                 Roo.MessageBox.confirm(
7738                     "Change requires confirmation",
7739                     action.result.errorMsg,
7740                     function(r) {
7741                         if (r != 'yes') {
7742                             return;
7743                         }
7744                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7745                     }
7746
7747                 );
7748                 */
7749
7750
7751                 return;
7752             }
7753
7754             Roo.callback(o.failure, o.scope, [this, action]);
7755             // show an error message if no failed handler is set..
7756             if (!this.hasListener('actionfailed')) {
7757                 Roo.log("need to add dialog support");
7758                 /*
7759                 Roo.MessageBox.alert("Error",
7760                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7761                         action.result.errorMsg :
7762                         "Saving Failed, please check your entries or try again"
7763                 );
7764                 */
7765             }
7766
7767             this.fireEvent('actionfailed', this, action);
7768         }
7769
7770     },
7771     /**
7772      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7773      * @param {String} id The value to search for
7774      * @return Field
7775      */
7776     findField : function(id){
7777         var items = this.getItems();
7778         var field = items.get(id);
7779         if(!field){
7780              items.each(function(f){
7781                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7782                     field = f;
7783                     return false;
7784                 }
7785                 return true;
7786             });
7787         }
7788         return field || null;
7789     },
7790      /**
7791      * Mark fields in this form invalid in bulk.
7792      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7793      * @return {BasicForm} this
7794      */
7795     markInvalid : function(errors){
7796         if(errors instanceof Array){
7797             for(var i = 0, len = errors.length; i < len; i++){
7798                 var fieldError = errors[i];
7799                 var f = this.findField(fieldError.id);
7800                 if(f){
7801                     f.markInvalid(fieldError.msg);
7802                 }
7803             }
7804         }else{
7805             var field, id;
7806             for(id in errors){
7807                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7808                     field.markInvalid(errors[id]);
7809                 }
7810             }
7811         }
7812         //Roo.each(this.childForms || [], function (f) {
7813         //    f.markInvalid(errors);
7814         //});
7815
7816         return this;
7817     },
7818
7819     /**
7820      * Set values for fields in this form in bulk.
7821      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7822      * @return {BasicForm} this
7823      */
7824     setValues : function(values){
7825         if(values instanceof Array){ // array of objects
7826             for(var i = 0, len = values.length; i < len; i++){
7827                 var v = values[i];
7828                 var f = this.findField(v.id);
7829                 if(f){
7830                     f.setValue(v.value);
7831                     if(this.trackResetOnLoad){
7832                         f.originalValue = f.getValue();
7833                     }
7834                 }
7835             }
7836         }else{ // object hash
7837             var field, id;
7838             for(id in values){
7839                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7840
7841                     if (field.setFromData &&
7842                         field.valueField &&
7843                         field.displayField &&
7844                         // combos' with local stores can
7845                         // be queried via setValue()
7846                         // to set their value..
7847                         (field.store && !field.store.isLocal)
7848                         ) {
7849                         // it's a combo
7850                         var sd = { };
7851                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7852                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7853                         field.setFromData(sd);
7854
7855                     } else {
7856                         field.setValue(values[id]);
7857                     }
7858
7859
7860                     if(this.trackResetOnLoad){
7861                         field.originalValue = field.getValue();
7862                     }
7863                 }
7864             }
7865         }
7866
7867         //Roo.each(this.childForms || [], function (f) {
7868         //    f.setValues(values);
7869         //});
7870
7871         return this;
7872     },
7873
7874     /**
7875      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7876      * they are returned as an array.
7877      * @param {Boolean} asString
7878      * @return {Object}
7879      */
7880     getValues : function(asString){
7881         //if (this.childForms) {
7882             // copy values from the child forms
7883         //    Roo.each(this.childForms, function (f) {
7884         //        this.setValues(f.getValues());
7885         //    }, this);
7886         //}
7887
7888
7889
7890         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7891         if(asString === true){
7892             return fs;
7893         }
7894         return Roo.urlDecode(fs);
7895     },
7896
7897     /**
7898      * Returns the fields in this form as an object with key/value pairs.
7899      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7900      * @return {Object}
7901      */
7902     getFieldValues : function(with_hidden)
7903     {
7904         var items = this.getItems();
7905         var ret = {};
7906         items.each(function(f){
7907             if (!f.getName()) {
7908                 return;
7909             }
7910             var v = f.getValue();
7911             if (f.inputType =='radio') {
7912                 if (typeof(ret[f.getName()]) == 'undefined') {
7913                     ret[f.getName()] = ''; // empty..
7914                 }
7915
7916                 if (!f.el.dom.checked) {
7917                     return;
7918
7919                 }
7920                 v = f.el.dom.value;
7921
7922             }
7923
7924             // not sure if this supported any more..
7925             if ((typeof(v) == 'object') && f.getRawValue) {
7926                 v = f.getRawValue() ; // dates..
7927             }
7928             // combo boxes where name != hiddenName...
7929             if (f.name != f.getName()) {
7930                 ret[f.name] = f.getRawValue();
7931             }
7932             ret[f.getName()] = v;
7933         });
7934
7935         return ret;
7936     },
7937
7938     /**
7939      * Clears all invalid messages in this form.
7940      * @return {BasicForm} this
7941      */
7942     clearInvalid : function(){
7943         var items = this.getItems();
7944
7945         items.each(function(f){
7946            f.clearInvalid();
7947         });
7948
7949
7950
7951         return this;
7952     },
7953
7954     /**
7955      * Resets this form.
7956      * @return {BasicForm} this
7957      */
7958     reset : function(){
7959         var items = this.getItems();
7960         items.each(function(f){
7961             f.reset();
7962         });
7963
7964         Roo.each(this.childForms || [], function (f) {
7965             f.reset();
7966         });
7967
7968
7969         return this;
7970     },
7971     getItems : function()
7972     {
7973         var r=new Roo.util.MixedCollection(false, function(o){
7974             return o.id || (o.id = Roo.id());
7975         });
7976         var iter = function(el) {
7977             if (el.inputEl) {
7978                 r.add(el);
7979             }
7980             if (!el.items) {
7981                 return;
7982             }
7983             Roo.each(el.items,function(e) {
7984                 iter(e);
7985             });
7986
7987
7988         };
7989
7990         iter(this);
7991         return r;
7992
7993
7994
7995
7996     }
7997
7998 });
7999 /*
8000  * Based on:
8001  * Ext JS Library 1.1.1
8002  * Copyright(c) 2006-2007, Ext JS, LLC.
8003  *
8004  * Originally Released Under LGPL - original licence link has changed is not relivant.
8005  *
8006  * Fork - LGPL
8007  * <script type="text/javascript">
8008  */
8009 /**
8010  * @class Roo.form.VTypes
8011  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8012  * @singleton
8013  */
8014 Roo.form.VTypes = function(){
8015     // closure these in so they are only created once.
8016     var alpha = /^[a-zA-Z_]+$/;
8017     var alphanum = /^[a-zA-Z0-9_]+$/;
8018     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8019     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8020
8021     // All these messages and functions are configurable
8022     return {
8023         /**
8024          * The function used to validate email addresses
8025          * @param {String} value The email address
8026          */
8027         'email' : function(v){
8028             return email.test(v);
8029         },
8030         /**
8031          * The error text to display when the email validation function returns false
8032          * @type String
8033          */
8034         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8035         /**
8036          * The keystroke filter mask to be applied on email input
8037          * @type RegExp
8038          */
8039         'emailMask' : /[a-z0-9_\.\-@]/i,
8040
8041         /**
8042          * The function used to validate URLs
8043          * @param {String} value The URL
8044          */
8045         'url' : function(v){
8046             return url.test(v);
8047         },
8048         /**
8049          * The error text to display when the url validation function returns false
8050          * @type String
8051          */
8052         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8053         
8054         /**
8055          * The function used to validate alpha values
8056          * @param {String} value The value
8057          */
8058         'alpha' : function(v){
8059             return alpha.test(v);
8060         },
8061         /**
8062          * The error text to display when the alpha validation function returns false
8063          * @type String
8064          */
8065         'alphaText' : 'This field should only contain letters and _',
8066         /**
8067          * The keystroke filter mask to be applied on alpha input
8068          * @type RegExp
8069          */
8070         'alphaMask' : /[a-z_]/i,
8071
8072         /**
8073          * The function used to validate alphanumeric values
8074          * @param {String} value The value
8075          */
8076         'alphanum' : function(v){
8077             return alphanum.test(v);
8078         },
8079         /**
8080          * The error text to display when the alphanumeric validation function returns false
8081          * @type String
8082          */
8083         'alphanumText' : 'This field should only contain letters, numbers and _',
8084         /**
8085          * The keystroke filter mask to be applied on alphanumeric input
8086          * @type RegExp
8087          */
8088         'alphanumMask' : /[a-z0-9_]/i
8089     };
8090 }();/*
8091  * - LGPL
8092  *
8093  * Input
8094  * 
8095  */
8096
8097 /**
8098  * @class Roo.bootstrap.Input
8099  * @extends Roo.bootstrap.Component
8100  * Bootstrap Input class
8101  * @cfg {Boolean} disabled is it disabled
8102  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8103  * @cfg {String} name name of the input
8104  * @cfg {string} fieldLabel - the label associated
8105  * @cfg {string} placeholder - placeholder to put in text.
8106  * @cfg {string}  before - input group add on before
8107  * @cfg {string} after - input group add on after
8108  * @cfg {string} size - (lg|sm) or leave empty..
8109  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8110  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8111  * @cfg {Number} md colspan out of 12 for computer-sized screens
8112  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8113  * @cfg {string} value default value of the input
8114  * @cfg {Number} labelWidth set the width of label (0-12)
8115  * @cfg {String} labelAlign (top|left)
8116  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8117  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8118  * @cfg {String} indicatorpos (left|right) default left
8119
8120  * @cfg {String} align (left|center|right) Default left
8121  * @cfg {Boolean} forceFeedback (true|false) Default false
8122  * 
8123  * 
8124  * 
8125  * 
8126  * @constructor
8127  * Create a new Input
8128  * @param {Object} config The config object
8129  */
8130
8131 Roo.bootstrap.Input = function(config){
8132     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8133    
8134         this.addEvents({
8135             /**
8136              * @event focus
8137              * Fires when this field receives input focus.
8138              * @param {Roo.form.Field} this
8139              */
8140             focus : true,
8141             /**
8142              * @event blur
8143              * Fires when this field loses input focus.
8144              * @param {Roo.form.Field} this
8145              */
8146             blur : true,
8147             /**
8148              * @event specialkey
8149              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8150              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8151              * @param {Roo.form.Field} this
8152              * @param {Roo.EventObject} e The event object
8153              */
8154             specialkey : true,
8155             /**
8156              * @event change
8157              * Fires just before the field blurs if the field value has changed.
8158              * @param {Roo.form.Field} this
8159              * @param {Mixed} newValue The new value
8160              * @param {Mixed} oldValue The original value
8161              */
8162             change : true,
8163             /**
8164              * @event invalid
8165              * Fires after the field has been marked as invalid.
8166              * @param {Roo.form.Field} this
8167              * @param {String} msg The validation message
8168              */
8169             invalid : true,
8170             /**
8171              * @event valid
8172              * Fires after the field has been validated with no errors.
8173              * @param {Roo.form.Field} this
8174              */
8175             valid : true,
8176              /**
8177              * @event keyup
8178              * Fires after the key up
8179              * @param {Roo.form.Field} this
8180              * @param {Roo.EventObject}  e The event Object
8181              */
8182             keyup : true
8183         });
8184 };
8185
8186 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8187      /**
8188      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8189       automatic validation (defaults to "keyup").
8190      */
8191     validationEvent : "keyup",
8192      /**
8193      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8194      */
8195     validateOnBlur : true,
8196     /**
8197      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8198      */
8199     validationDelay : 250,
8200      /**
8201      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8202      */
8203     focusClass : "x-form-focus",  // not needed???
8204     
8205        
8206     /**
8207      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8208      */
8209     invalidClass : "has-warning",
8210     
8211     /**
8212      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8213      */
8214     validClass : "has-success",
8215     
8216     /**
8217      * @cfg {Boolean} hasFeedback (true|false) default true
8218      */
8219     hasFeedback : true,
8220     
8221     /**
8222      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8223      */
8224     invalidFeedbackClass : "glyphicon-warning-sign",
8225     
8226     /**
8227      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8228      */
8229     validFeedbackClass : "glyphicon-ok",
8230     
8231     /**
8232      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8233      */
8234     selectOnFocus : false,
8235     
8236      /**
8237      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8238      */
8239     maskRe : null,
8240        /**
8241      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8242      */
8243     vtype : null,
8244     
8245       /**
8246      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8247      */
8248     disableKeyFilter : false,
8249     
8250        /**
8251      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8252      */
8253     disabled : false,
8254      /**
8255      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8256      */
8257     allowBlank : true,
8258     /**
8259      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8260      */
8261     blankText : "This field is required",
8262     
8263      /**
8264      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8265      */
8266     minLength : 0,
8267     /**
8268      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8269      */
8270     maxLength : Number.MAX_VALUE,
8271     /**
8272      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8273      */
8274     minLengthText : "The minimum length for this field is {0}",
8275     /**
8276      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8277      */
8278     maxLengthText : "The maximum length for this field is {0}",
8279   
8280     
8281     /**
8282      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8283      * If available, this function will be called only after the basic validators all return true, and will be passed the
8284      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8285      */
8286     validator : null,
8287     /**
8288      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8289      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8290      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8291      */
8292     regex : null,
8293     /**
8294      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8295      */
8296     regexText : "",
8297     
8298     autocomplete: false,
8299     
8300     
8301     fieldLabel : '',
8302     inputType : 'text',
8303     
8304     name : false,
8305     placeholder: false,
8306     before : false,
8307     after : false,
8308     size : false,
8309     hasFocus : false,
8310     preventMark: false,
8311     isFormField : true,
8312     value : '',
8313     labelWidth : 2,
8314     labelAlign : false,
8315     readOnly : false,
8316     align : false,
8317     formatedValue : false,
8318     forceFeedback : false,
8319     
8320     indicatorpos : 'left',
8321     
8322     parentLabelAlign : function()
8323     {
8324         var parent = this;
8325         while (parent.parent()) {
8326             parent = parent.parent();
8327             if (typeof(parent.labelAlign) !='undefined') {
8328                 return parent.labelAlign;
8329             }
8330         }
8331         return 'left';
8332         
8333     },
8334     
8335     getAutoCreate : function()
8336     {
8337         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8338         
8339         var id = Roo.id();
8340         
8341         var cfg = {};
8342         
8343         if(this.inputType != 'hidden'){
8344             cfg.cls = 'form-group' //input-group
8345         }
8346         
8347         var input =  {
8348             tag: 'input',
8349             id : id,
8350             type : this.inputType,
8351             value : this.value,
8352             cls : 'form-control',
8353             placeholder : this.placeholder || '',
8354             autocomplete : this.autocomplete || 'new-password'
8355         };
8356         
8357         if(this.align){
8358             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8359         }
8360         
8361         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8362             input.maxLength = this.maxLength;
8363         }
8364         
8365         if (this.disabled) {
8366             input.disabled=true;
8367         }
8368         
8369         if (this.readOnly) {
8370             input.readonly=true;
8371         }
8372         
8373         if (this.name) {
8374             input.name = this.name;
8375         }
8376         
8377         if (this.size) {
8378             input.cls += ' input-' + this.size;
8379         }
8380         
8381         var settings=this;
8382         ['xs','sm','md','lg'].map(function(size){
8383             if (settings[size]) {
8384                 cfg.cls += ' col-' + size + '-' + settings[size];
8385             }
8386         });
8387         
8388         var inputblock = input;
8389         
8390         var feedback = {
8391             tag: 'span',
8392             cls: 'glyphicon form-control-feedback'
8393         };
8394             
8395         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8396             
8397             inputblock = {
8398                 cls : 'has-feedback',
8399                 cn :  [
8400                     input,
8401                     feedback
8402                 ] 
8403             };  
8404         }
8405         
8406         if (this.before || this.after) {
8407             
8408             inputblock = {
8409                 cls : 'input-group',
8410                 cn :  [] 
8411             };
8412             
8413             if (this.before && typeof(this.before) == 'string') {
8414                 
8415                 inputblock.cn.push({
8416                     tag :'span',
8417                     cls : 'roo-input-before input-group-addon',
8418                     html : this.before
8419                 });
8420             }
8421             if (this.before && typeof(this.before) == 'object') {
8422                 this.before = Roo.factory(this.before);
8423                 
8424                 inputblock.cn.push({
8425                     tag :'span',
8426                     cls : 'roo-input-before input-group-' +
8427                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8428                 });
8429             }
8430             
8431             inputblock.cn.push(input);
8432             
8433             if (this.after && typeof(this.after) == 'string') {
8434                 inputblock.cn.push({
8435                     tag :'span',
8436                     cls : 'roo-input-after input-group-addon',
8437                     html : this.after
8438                 });
8439             }
8440             if (this.after && typeof(this.after) == 'object') {
8441                 this.after = Roo.factory(this.after);
8442                 
8443                 inputblock.cn.push({
8444                     tag :'span',
8445                     cls : 'roo-input-after input-group-' +
8446                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8447                 });
8448             }
8449             
8450             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8451                 inputblock.cls += ' has-feedback';
8452                 inputblock.cn.push(feedback);
8453             }
8454         };
8455         
8456         if (align ==='left' && this.fieldLabel.length) {
8457             
8458             cfg.cn = [
8459                 {
8460                     tag : 'i',
8461                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8462                     tooltip : 'This field is required'
8463                 },
8464                 {
8465                     tag: 'label',
8466                     'for' :  id,
8467                     cls : 'control-label col-sm-' + this.labelWidth,
8468                     html : this.fieldLabel
8469
8470                 },
8471                 {
8472                     cls : "col-sm-" + (12 - this.labelWidth), 
8473                     cn: [
8474                         inputblock
8475                     ]
8476                 }
8477
8478             ];
8479             
8480             if(this.indicatorpos == 'right'){
8481                 cfg.cn = [
8482                     {
8483                         tag: 'label',
8484                         'for' :  id,
8485                         cls : 'control-label col-sm-' + this.labelWidth,
8486                         html : this.fieldLabel
8487
8488                     },
8489                     {
8490                         tag : 'i',
8491                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8492                         tooltip : 'This field is required'
8493                     },
8494                     {
8495                         cls : "col-sm-" + (12 - this.labelWidth), 
8496                         cn: [
8497                             inputblock
8498                         ]
8499                     }
8500
8501                 ];
8502             }
8503             
8504         } else if ( this.fieldLabel.length) {
8505                 
8506             cfg.cn = [
8507                 {
8508                     tag : 'i',
8509                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8510                     tooltip : 'This field is required'
8511                 },
8512                 {
8513                     tag: 'label',
8514                    //cls : 'input-group-addon',
8515                     html : this.fieldLabel
8516
8517                 },
8518
8519                inputblock
8520
8521            ];
8522            
8523            if(this.indicatorpos == 'right'){
8524                 
8525                 cfg.cn = [
8526                     {
8527                         tag: 'label',
8528                        //cls : 'input-group-addon',
8529                         html : this.fieldLabel
8530
8531                     },
8532                     {
8533                         tag : 'i',
8534                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8535                         tooltip : 'This field is required'
8536                     },
8537
8538                    inputblock
8539
8540                ];
8541
8542             }
8543
8544         } else {
8545             
8546             cfg.cn = [
8547
8548                     inputblock
8549
8550             ];
8551                 
8552                 
8553         };
8554         
8555         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8556            cfg.cls += ' navbar-form';
8557         }
8558         
8559         if (this.parentType === 'NavGroup') {
8560            cfg.cls += ' navbar-form';
8561            cfg.tag = 'li';
8562         }
8563         
8564         return cfg;
8565         
8566     },
8567     /**
8568      * return the real input element.
8569      */
8570     inputEl: function ()
8571     {
8572         return this.el.select('input.form-control',true).first();
8573     },
8574     
8575     tooltipEl : function()
8576     {
8577         return this.inputEl();
8578     },
8579     
8580     indicatorEl : function()
8581     {
8582         var indicator = this.el.select('i.roo-required-indicator',true).first();
8583         
8584         if(!indicator){
8585             return false;
8586         }
8587         
8588         return indicator;
8589         
8590     },
8591     
8592     setDisabled : function(v)
8593     {
8594         var i  = this.inputEl().dom;
8595         if (!v) {
8596             i.removeAttribute('disabled');
8597             return;
8598             
8599         }
8600         i.setAttribute('disabled','true');
8601     },
8602     initEvents : function()
8603     {
8604           
8605         this.inputEl().on("keydown" , this.fireKey,  this);
8606         this.inputEl().on("focus", this.onFocus,  this);
8607         this.inputEl().on("blur", this.onBlur,  this);
8608         
8609         this.inputEl().relayEvent('keyup', this);
8610         
8611         this.indicator = this.indicatorEl();
8612         
8613         if(this.indicator){
8614             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8615             this.indicator.hide();
8616         }
8617  
8618         // reference to original value for reset
8619         this.originalValue = this.getValue();
8620         //Roo.form.TextField.superclass.initEvents.call(this);
8621         if(this.validationEvent == 'keyup'){
8622             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8623             this.inputEl().on('keyup', this.filterValidation, this);
8624         }
8625         else if(this.validationEvent !== false){
8626             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8627         }
8628         
8629         if(this.selectOnFocus){
8630             this.on("focus", this.preFocus, this);
8631             
8632         }
8633         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8634             this.inputEl().on("keypress", this.filterKeys, this);
8635         } else {
8636             this.inputEl().relayEvent('keypress', this);
8637         }
8638        /* if(this.grow){
8639             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8640             this.el.on("click", this.autoSize,  this);
8641         }
8642         */
8643         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8644             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8645         }
8646         
8647         if (typeof(this.before) == 'object') {
8648             this.before.render(this.el.select('.roo-input-before',true).first());
8649         }
8650         if (typeof(this.after) == 'object') {
8651             this.after.render(this.el.select('.roo-input-after',true).first());
8652         }
8653         
8654         
8655     },
8656     filterValidation : function(e){
8657         if(!e.isNavKeyPress()){
8658             this.validationTask.delay(this.validationDelay);
8659         }
8660     },
8661      /**
8662      * Validates the field value
8663      * @return {Boolean} True if the value is valid, else false
8664      */
8665     validate : function(){
8666         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8667         if(this.disabled || this.validateValue(this.getRawValue())){
8668             this.markValid();
8669             return true;
8670         }
8671         
8672         this.markInvalid();
8673         return false;
8674     },
8675     
8676     
8677     /**
8678      * Validates a value according to the field's validation rules and marks the field as invalid
8679      * if the validation fails
8680      * @param {Mixed} value The value to validate
8681      * @return {Boolean} True if the value is valid, else false
8682      */
8683     validateValue : function(value){
8684         if(value.length < 1)  { // if it's blank
8685             if(this.allowBlank){
8686                 return true;
8687             }
8688             return false;
8689         }
8690         
8691         if(value.length < this.minLength){
8692             return false;
8693         }
8694         if(value.length > this.maxLength){
8695             return false;
8696         }
8697         if(this.vtype){
8698             var vt = Roo.form.VTypes;
8699             if(!vt[this.vtype](value, this)){
8700                 return false;
8701             }
8702         }
8703         if(typeof this.validator == "function"){
8704             var msg = this.validator(value);
8705             if(msg !== true){
8706                 return false;
8707             }
8708         }
8709         
8710         if(this.regex && !this.regex.test(value)){
8711             return false;
8712         }
8713         
8714         return true;
8715     },
8716
8717     
8718     
8719      // private
8720     fireKey : function(e){
8721         //Roo.log('field ' + e.getKey());
8722         if(e.isNavKeyPress()){
8723             this.fireEvent("specialkey", this, e);
8724         }
8725     },
8726     focus : function (selectText){
8727         if(this.rendered){
8728             this.inputEl().focus();
8729             if(selectText === true){
8730                 this.inputEl().dom.select();
8731             }
8732         }
8733         return this;
8734     } ,
8735     
8736     onFocus : function(){
8737         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8738            // this.el.addClass(this.focusClass);
8739         }
8740         if(!this.hasFocus){
8741             this.hasFocus = true;
8742             this.startValue = this.getValue();
8743             this.fireEvent("focus", this);
8744         }
8745     },
8746     
8747     beforeBlur : Roo.emptyFn,
8748
8749     
8750     // private
8751     onBlur : function(){
8752         this.beforeBlur();
8753         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8754             //this.el.removeClass(this.focusClass);
8755         }
8756         this.hasFocus = false;
8757         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8758             this.validate();
8759         }
8760         var v = this.getValue();
8761         if(String(v) !== String(this.startValue)){
8762             this.fireEvent('change', this, v, this.startValue);
8763         }
8764         this.fireEvent("blur", this);
8765     },
8766     
8767     /**
8768      * Resets the current field value to the originally loaded value and clears any validation messages
8769      */
8770     reset : function(){
8771         this.setValue(this.originalValue);
8772         this.validate();
8773     },
8774      /**
8775      * Returns the name of the field
8776      * @return {Mixed} name The name field
8777      */
8778     getName: function(){
8779         return this.name;
8780     },
8781      /**
8782      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8783      * @return {Mixed} value The field value
8784      */
8785     getValue : function(){
8786         
8787         var v = this.inputEl().getValue();
8788         
8789         return v;
8790     },
8791     /**
8792      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8793      * @return {Mixed} value The field value
8794      */
8795     getRawValue : function(){
8796         var v = this.inputEl().getValue();
8797         
8798         return v;
8799     },
8800     
8801     /**
8802      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8803      * @param {Mixed} value The value to set
8804      */
8805     setRawValue : function(v){
8806         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8807     },
8808     
8809     selectText : function(start, end){
8810         var v = this.getRawValue();
8811         if(v.length > 0){
8812             start = start === undefined ? 0 : start;
8813             end = end === undefined ? v.length : end;
8814             var d = this.inputEl().dom;
8815             if(d.setSelectionRange){
8816                 d.setSelectionRange(start, end);
8817             }else if(d.createTextRange){
8818                 var range = d.createTextRange();
8819                 range.moveStart("character", start);
8820                 range.moveEnd("character", v.length-end);
8821                 range.select();
8822             }
8823         }
8824     },
8825     
8826     /**
8827      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8828      * @param {Mixed} value The value to set
8829      */
8830     setValue : function(v){
8831         this.value = v;
8832         if(this.rendered){
8833             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8834             this.validate();
8835         }
8836     },
8837     
8838     /*
8839     processValue : function(value){
8840         if(this.stripCharsRe){
8841             var newValue = value.replace(this.stripCharsRe, '');
8842             if(newValue !== value){
8843                 this.setRawValue(newValue);
8844                 return newValue;
8845             }
8846         }
8847         return value;
8848     },
8849   */
8850     preFocus : function(){
8851         
8852         if(this.selectOnFocus){
8853             this.inputEl().dom.select();
8854         }
8855     },
8856     filterKeys : function(e){
8857         var k = e.getKey();
8858         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8859             return;
8860         }
8861         var c = e.getCharCode(), cc = String.fromCharCode(c);
8862         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8863             return;
8864         }
8865         if(!this.maskRe.test(cc)){
8866             e.stopEvent();
8867         }
8868     },
8869      /**
8870      * Clear any invalid styles/messages for this field
8871      */
8872     clearInvalid : function(){
8873         
8874         if(!this.el || this.preventMark){ // not rendered
8875             return;
8876         }
8877         
8878         if(this.indicator){
8879             this.indicator.hide();
8880         }
8881         
8882         this.el.removeClass(this.invalidClass);
8883         
8884         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8885             
8886             var feedback = this.el.select('.form-control-feedback', true).first();
8887             
8888             if(feedback){
8889                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8890             }
8891             
8892         }
8893         
8894         this.fireEvent('valid', this);
8895     },
8896     
8897      /**
8898      * Mark this field as valid
8899      */
8900     markValid : function()
8901     {
8902         if(!this.el  || this.preventMark){ // not rendered
8903             return;
8904         }
8905         
8906         this.el.removeClass([this.invalidClass, this.validClass]);
8907         
8908         var feedback = this.el.select('.form-control-feedback', true).first();
8909             
8910         if(feedback){
8911             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8912         }
8913
8914         if(this.disabled){
8915             return;
8916         }
8917         
8918         if(this.allowBlank && !this.getRawValue().length){
8919             return;
8920         }
8921         
8922         if(this.indicator){
8923             this.indicator.hide();
8924         }
8925         
8926         this.el.addClass(this.validClass);
8927         
8928         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8929             
8930             var feedback = this.el.select('.form-control-feedback', true).first();
8931             
8932             if(feedback){
8933                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8934                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8935             }
8936             
8937         }
8938         
8939         this.fireEvent('valid', this);
8940     },
8941     
8942      /**
8943      * Mark this field as invalid
8944      * @param {String} msg The validation message
8945      */
8946     markInvalid : function(msg)
8947     {
8948         if(!this.el  || this.preventMark){ // not rendered
8949             return;
8950         }
8951         
8952         this.el.removeClass([this.invalidClass, this.validClass]);
8953         
8954         var feedback = this.el.select('.form-control-feedback', true).first();
8955             
8956         if(feedback){
8957             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8958         }
8959
8960         if(this.disabled){
8961             return;
8962         }
8963         
8964         if(this.allowBlank && !this.getRawValue().length){
8965             return;
8966         }
8967         
8968         if(this.indicator){
8969             this.indicator.show();
8970         }
8971         
8972         this.el.addClass(this.invalidClass);
8973         
8974         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8975             
8976             var feedback = this.el.select('.form-control-feedback', true).first();
8977             
8978             if(feedback){
8979                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8980                 
8981                 if(this.getValue().length || this.forceFeedback){
8982                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8983                 }
8984                 
8985             }
8986             
8987         }
8988         
8989         this.fireEvent('invalid', this, msg);
8990     },
8991     // private
8992     SafariOnKeyDown : function(event)
8993     {
8994         // this is a workaround for a password hang bug on chrome/ webkit.
8995         if (this.inputEl().dom.type != 'password') {
8996             return;
8997         }
8998         
8999         var isSelectAll = false;
9000         
9001         if(this.inputEl().dom.selectionEnd > 0){
9002             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9003         }
9004         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9005             event.preventDefault();
9006             this.setValue('');
9007             return;
9008         }
9009         
9010         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9011             
9012             event.preventDefault();
9013             // this is very hacky as keydown always get's upper case.
9014             //
9015             var cc = String.fromCharCode(event.getCharCode());
9016             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9017             
9018         }
9019     },
9020     adjustWidth : function(tag, w){
9021         tag = tag.toLowerCase();
9022         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9023             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9024                 if(tag == 'input'){
9025                     return w + 2;
9026                 }
9027                 if(tag == 'textarea'){
9028                     return w-2;
9029                 }
9030             }else if(Roo.isOpera){
9031                 if(tag == 'input'){
9032                     return w + 2;
9033                 }
9034                 if(tag == 'textarea'){
9035                     return w-2;
9036                 }
9037             }
9038         }
9039         return w;
9040     }
9041     
9042 });
9043
9044  
9045 /*
9046  * - LGPL
9047  *
9048  * Input
9049  * 
9050  */
9051
9052 /**
9053  * @class Roo.bootstrap.TextArea
9054  * @extends Roo.bootstrap.Input
9055  * Bootstrap TextArea class
9056  * @cfg {Number} cols Specifies the visible width of a text area
9057  * @cfg {Number} rows Specifies the visible number of lines in a text area
9058  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9059  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9060  * @cfg {string} html text
9061  * 
9062  * @constructor
9063  * Create a new TextArea
9064  * @param {Object} config The config object
9065  */
9066
9067 Roo.bootstrap.TextArea = function(config){
9068     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9069    
9070 };
9071
9072 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9073      
9074     cols : false,
9075     rows : 5,
9076     readOnly : false,
9077     warp : 'soft',
9078     resize : false,
9079     value: false,
9080     html: false,
9081     
9082     getAutoCreate : function(){
9083         
9084         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9085         
9086         var id = Roo.id();
9087         
9088         var cfg = {};
9089         
9090         var input =  {
9091             tag: 'textarea',
9092             id : id,
9093             warp : this.warp,
9094             rows : this.rows,
9095             value : this.value || '',
9096             html: this.html || '',
9097             cls : 'form-control',
9098             placeholder : this.placeholder || '' 
9099             
9100         };
9101         
9102         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9103             input.maxLength = this.maxLength;
9104         }
9105         
9106         if(this.resize){
9107             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9108         }
9109         
9110         if(this.cols){
9111             input.cols = this.cols;
9112         }
9113         
9114         if (this.readOnly) {
9115             input.readonly = true;
9116         }
9117         
9118         if (this.name) {
9119             input.name = this.name;
9120         }
9121         
9122         if (this.size) {
9123             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9124         }
9125         
9126         var settings=this;
9127         ['xs','sm','md','lg'].map(function(size){
9128             if (settings[size]) {
9129                 cfg.cls += ' col-' + size + '-' + settings[size];
9130             }
9131         });
9132         
9133         var inputblock = input;
9134         
9135         if(this.hasFeedback && !this.allowBlank){
9136             
9137             var feedback = {
9138                 tag: 'span',
9139                 cls: 'glyphicon form-control-feedback'
9140             };
9141
9142             inputblock = {
9143                 cls : 'has-feedback',
9144                 cn :  [
9145                     input,
9146                     feedback
9147                 ] 
9148             };  
9149         }
9150         
9151         
9152         if (this.before || this.after) {
9153             
9154             inputblock = {
9155                 cls : 'input-group',
9156                 cn :  [] 
9157             };
9158             if (this.before) {
9159                 inputblock.cn.push({
9160                     tag :'span',
9161                     cls : 'input-group-addon',
9162                     html : this.before
9163                 });
9164             }
9165             
9166             inputblock.cn.push(input);
9167             
9168             if(this.hasFeedback && !this.allowBlank){
9169                 inputblock.cls += ' has-feedback';
9170                 inputblock.cn.push(feedback);
9171             }
9172             
9173             if (this.after) {
9174                 inputblock.cn.push({
9175                     tag :'span',
9176                     cls : 'input-group-addon',
9177                     html : this.after
9178                 });
9179             }
9180             
9181         }
9182         
9183         if (align ==='left' && this.fieldLabel.length) {
9184 //                Roo.log("left and has label");
9185                 cfg.cn = [
9186                     
9187                     {
9188                         tag: 'label',
9189                         'for' :  id,
9190                         cls : 'control-label col-sm-' + this.labelWidth,
9191                         html : this.fieldLabel
9192                         
9193                     },
9194                     {
9195                         cls : "col-sm-" + (12 - this.labelWidth), 
9196                         cn: [
9197                             inputblock
9198                         ]
9199                     }
9200                     
9201                 ];
9202         } else if ( this.fieldLabel.length) {
9203 //                Roo.log(" label");
9204                  cfg.cn = [
9205                    
9206                     {
9207                         tag: 'label',
9208                         //cls : 'input-group-addon',
9209                         html : this.fieldLabel
9210                         
9211                     },
9212                     
9213                     inputblock
9214                     
9215                 ];
9216
9217         } else {
9218             
9219 //                   Roo.log(" no label && no align");
9220                 cfg.cn = [
9221                     
9222                         inputblock
9223                     
9224                 ];
9225                 
9226                 
9227         }
9228         
9229         if (this.disabled) {
9230             input.disabled=true;
9231         }
9232         
9233         return cfg;
9234         
9235     },
9236     /**
9237      * return the real textarea element.
9238      */
9239     inputEl: function ()
9240     {
9241         return this.el.select('textarea.form-control',true).first();
9242     },
9243     
9244     /**
9245      * Clear any invalid styles/messages for this field
9246      */
9247     clearInvalid : function()
9248     {
9249         
9250         if(!this.el || this.preventMark){ // not rendered
9251             return;
9252         }
9253         
9254         var label = this.el.select('label', true).first();
9255         var icon = this.el.select('i.fa-star', true).first();
9256         
9257         if(label && icon){
9258             icon.remove();
9259         }
9260         
9261         this.el.removeClass(this.invalidClass);
9262         
9263         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9264             
9265             var feedback = this.el.select('.form-control-feedback', true).first();
9266             
9267             if(feedback){
9268                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9269             }
9270             
9271         }
9272         
9273         this.fireEvent('valid', this);
9274     },
9275     
9276      /**
9277      * Mark this field as valid
9278      */
9279     markValid : function()
9280     {
9281         if(!this.el  || this.preventMark){ // not rendered
9282             return;
9283         }
9284         
9285         this.el.removeClass([this.invalidClass, this.validClass]);
9286         
9287         var feedback = this.el.select('.form-control-feedback', true).first();
9288             
9289         if(feedback){
9290             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9291         }
9292
9293         if(this.disabled || this.allowBlank){
9294             return;
9295         }
9296         
9297         var label = this.el.select('label', true).first();
9298         var icon = this.el.select('i.fa-star', true).first();
9299         
9300         if(label && icon){
9301             icon.remove();
9302         }
9303         
9304         this.el.addClass(this.validClass);
9305         
9306         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9307             
9308             var feedback = this.el.select('.form-control-feedback', true).first();
9309             
9310             if(feedback){
9311                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9312                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9313             }
9314             
9315         }
9316         
9317         this.fireEvent('valid', this);
9318     },
9319     
9320      /**
9321      * Mark this field as invalid
9322      * @param {String} msg The validation message
9323      */
9324     markInvalid : function(msg)
9325     {
9326         if(!this.el  || this.preventMark){ // not rendered
9327             return;
9328         }
9329         
9330         this.el.removeClass([this.invalidClass, this.validClass]);
9331         
9332         var feedback = this.el.select('.form-control-feedback', true).first();
9333             
9334         if(feedback){
9335             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9336         }
9337
9338         if(this.disabled || this.allowBlank){
9339             return;
9340         }
9341         
9342         var label = this.el.select('label', true).first();
9343         var icon = this.el.select('i.fa-star', true).first();
9344         
9345         if(!this.getValue().length && label && !icon){
9346             this.el.createChild({
9347                 tag : 'i',
9348                 cls : 'text-danger fa fa-lg fa-star',
9349                 tooltip : 'This field is required',
9350                 style : 'margin-right:5px;'
9351             }, label, true);
9352         }
9353
9354         this.el.addClass(this.invalidClass);
9355         
9356         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9357             
9358             var feedback = this.el.select('.form-control-feedback', true).first();
9359             
9360             if(feedback){
9361                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9362                 
9363                 if(this.getValue().length || this.forceFeedback){
9364                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9365                 }
9366                 
9367             }
9368             
9369         }
9370         
9371         this.fireEvent('invalid', this, msg);
9372     }
9373 });
9374
9375  
9376 /*
9377  * - LGPL
9378  *
9379  * trigger field - base class for combo..
9380  * 
9381  */
9382  
9383 /**
9384  * @class Roo.bootstrap.TriggerField
9385  * @extends Roo.bootstrap.Input
9386  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9387  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9388  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9389  * for which you can provide a custom implementation.  For example:
9390  * <pre><code>
9391 var trigger = new Roo.bootstrap.TriggerField();
9392 trigger.onTriggerClick = myTriggerFn;
9393 trigger.applyTo('my-field');
9394 </code></pre>
9395  *
9396  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9397  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9398  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9399  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9400  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9401
9402  * @constructor
9403  * Create a new TriggerField.
9404  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9405  * to the base TextField)
9406  */
9407 Roo.bootstrap.TriggerField = function(config){
9408     this.mimicing = false;
9409     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9410 };
9411
9412 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9413     /**
9414      * @cfg {String} triggerClass A CSS class to apply to the trigger
9415      */
9416      /**
9417      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9418      */
9419     hideTrigger:false,
9420
9421     /**
9422      * @cfg {Boolean} removable (true|false) special filter default false
9423      */
9424     removable : false,
9425     
9426     /** @cfg {Boolean} grow @hide */
9427     /** @cfg {Number} growMin @hide */
9428     /** @cfg {Number} growMax @hide */
9429
9430     /**
9431      * @hide 
9432      * @method
9433      */
9434     autoSize: Roo.emptyFn,
9435     // private
9436     monitorTab : true,
9437     // private
9438     deferHeight : true,
9439
9440     
9441     actionMode : 'wrap',
9442     
9443     caret : false,
9444     
9445     
9446     getAutoCreate : function(){
9447        
9448         var align = this.labelAlign || this.parentLabelAlign();
9449         
9450         var id = Roo.id();
9451         
9452         var cfg = {
9453             cls: 'form-group' //input-group
9454         };
9455         
9456         
9457         var input =  {
9458             tag: 'input',
9459             id : id,
9460             type : this.inputType,
9461             cls : 'form-control',
9462             autocomplete: 'new-password',
9463             placeholder : this.placeholder || '' 
9464             
9465         };
9466         if (this.name) {
9467             input.name = this.name;
9468         }
9469         if (this.size) {
9470             input.cls += ' input-' + this.size;
9471         }
9472         
9473         if (this.disabled) {
9474             input.disabled=true;
9475         }
9476         
9477         var inputblock = input;
9478         
9479         if(this.hasFeedback && !this.allowBlank){
9480             
9481             var feedback = {
9482                 tag: 'span',
9483                 cls: 'glyphicon form-control-feedback'
9484             };
9485             
9486             if(this.removable && !this.editable && !this.tickable){
9487                 inputblock = {
9488                     cls : 'has-feedback',
9489                     cn :  [
9490                         inputblock,
9491                         {
9492                             tag: 'button',
9493                             html : 'x',
9494                             cls : 'roo-combo-removable-btn close'
9495                         },
9496                         feedback
9497                     ] 
9498                 };
9499             } else {
9500                 inputblock = {
9501                     cls : 'has-feedback',
9502                     cn :  [
9503                         inputblock,
9504                         feedback
9505                     ] 
9506                 };
9507             }
9508
9509         } else {
9510             if(this.removable && !this.editable && !this.tickable){
9511                 inputblock = {
9512                     cls : 'roo-removable',
9513                     cn :  [
9514                         inputblock,
9515                         {
9516                             tag: 'button',
9517                             html : 'x',
9518                             cls : 'roo-combo-removable-btn close'
9519                         }
9520                     ] 
9521                 };
9522             }
9523         }
9524         
9525         if (this.before || this.after) {
9526             
9527             inputblock = {
9528                 cls : 'input-group',
9529                 cn :  [] 
9530             };
9531             if (this.before) {
9532                 inputblock.cn.push({
9533                     tag :'span',
9534                     cls : 'input-group-addon',
9535                     html : this.before
9536                 });
9537             }
9538             
9539             inputblock.cn.push(input);
9540             
9541             if(this.hasFeedback && !this.allowBlank){
9542                 inputblock.cls += ' has-feedback';
9543                 inputblock.cn.push(feedback);
9544             }
9545             
9546             if (this.after) {
9547                 inputblock.cn.push({
9548                     tag :'span',
9549                     cls : 'input-group-addon',
9550                     html : this.after
9551                 });
9552             }
9553             
9554         };
9555         
9556         var box = {
9557             tag: 'div',
9558             cn: [
9559                 {
9560                     tag: 'input',
9561                     type : 'hidden',
9562                     cls: 'form-hidden-field'
9563                 },
9564                 inputblock
9565             ]
9566             
9567         };
9568         
9569         if(this.multiple){
9570             box = {
9571                 tag: 'div',
9572                 cn: [
9573                     {
9574                         tag: 'input',
9575                         type : 'hidden',
9576                         cls: 'form-hidden-field'
9577                     },
9578                     {
9579                         tag: 'ul',
9580                         cls: 'roo-select2-choices',
9581                         cn:[
9582                             {
9583                                 tag: 'li',
9584                                 cls: 'roo-select2-search-field',
9585                                 cn: [
9586
9587                                     inputblock
9588                                 ]
9589                             }
9590                         ]
9591                     }
9592                 ]
9593             }
9594         };
9595         
9596         var combobox = {
9597             cls: 'roo-select2-container input-group',
9598             cn: [
9599                 box
9600 //                {
9601 //                    tag: 'ul',
9602 //                    cls: 'typeahead typeahead-long dropdown-menu',
9603 //                    style: 'display:none'
9604 //                }
9605             ]
9606         };
9607         
9608         if(!this.multiple && this.showToggleBtn){
9609             
9610             var caret = {
9611                         tag: 'span',
9612                         cls: 'caret'
9613              };
9614             if (this.caret != false) {
9615                 caret = {
9616                      tag: 'i',
9617                      cls: 'fa fa-' + this.caret
9618                 };
9619                 
9620             }
9621             
9622             combobox.cn.push({
9623                 tag :'span',
9624                 cls : 'input-group-addon btn dropdown-toggle',
9625                 cn : [
9626                     caret,
9627                     {
9628                         tag: 'span',
9629                         cls: 'combobox-clear',
9630                         cn  : [
9631                             {
9632                                 tag : 'i',
9633                                 cls: 'icon-remove'
9634                             }
9635                         ]
9636                     }
9637                 ]
9638
9639             })
9640         }
9641         
9642         if(this.multiple){
9643             combobox.cls += ' roo-select2-container-multi';
9644         }
9645         
9646         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9647             
9648 //                Roo.log("left and has label");
9649             cfg.cn = [
9650                 {
9651                     tag : 'i',
9652                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9653                     tooltip : 'This field is required'
9654                 },
9655                 {
9656                     tag: 'label',
9657                     'for' :  id,
9658                     cls : 'control-label col-sm-' + this.labelWidth,
9659                     html : this.fieldLabel
9660
9661                 },
9662                 {
9663                     cls : "col-sm-" + (12 - this.labelWidth), 
9664                     cn: [
9665                         combobox
9666                     ]
9667                 }
9668
9669             ];
9670             
9671             if(this.indicatorpos == 'right'){
9672                 cfg.cn = [
9673                     {
9674                         tag: 'label',
9675                         'for' :  id,
9676                         cls : 'control-label col-sm-' + this.labelWidth,
9677                         html : this.fieldLabel
9678
9679                     },
9680                     {
9681                         tag : 'i',
9682                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9683                         tooltip : 'This field is required'
9684                     },
9685                     {
9686                         cls : "col-sm-" + (12 - this.labelWidth), 
9687                         cn: [
9688                             combobox
9689                         ]
9690                     }
9691
9692                 ];
9693             }
9694             
9695         } else if ( this.fieldLabel.length) {
9696 //                Roo.log(" label");
9697             cfg.cn = [
9698                 {
9699                    tag : 'i',
9700                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9701                    tooltip : 'This field is required'
9702                },
9703                {
9704                    tag: 'label',
9705                    //cls : 'input-group-addon',
9706                    html : this.fieldLabel
9707
9708                },
9709
9710                combobox
9711
9712             ];
9713             
9714             if(this.indicatorpos == 'right'){
9715                 
9716                 cfg.cn = [
9717                     {
9718                        tag: 'label',
9719                        //cls : 'input-group-addon',
9720                        html : this.fieldLabel
9721
9722                     },
9723                     {
9724                        tag : 'i',
9725                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9726                        tooltip : 'This field is required'
9727                     },
9728                     
9729                     combobox
9730
9731                 ];
9732
9733             }
9734
9735         } else {
9736             
9737 //                Roo.log(" no label && no align");
9738                 cfg = combobox
9739                      
9740                 
9741         }
9742          
9743         var settings=this;
9744         ['xs','sm','md','lg'].map(function(size){
9745             if (settings[size]) {
9746                 cfg.cls += ' col-' + size + '-' + settings[size];
9747             }
9748         });
9749         
9750         return cfg;
9751         
9752     },
9753     
9754     
9755     
9756     // private
9757     onResize : function(w, h){
9758 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9759 //        if(typeof w == 'number'){
9760 //            var x = w - this.trigger.getWidth();
9761 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9762 //            this.trigger.setStyle('left', x+'px');
9763 //        }
9764     },
9765
9766     // private
9767     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9768
9769     // private
9770     getResizeEl : function(){
9771         return this.inputEl();
9772     },
9773
9774     // private
9775     getPositionEl : function(){
9776         return this.inputEl();
9777     },
9778
9779     // private
9780     alignErrorIcon : function(){
9781         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9782     },
9783
9784     // private
9785     initEvents : function(){
9786         
9787         this.createList();
9788         
9789         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9790         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9791         if(!this.multiple && this.showToggleBtn){
9792             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9793             if(this.hideTrigger){
9794                 this.trigger.setDisplayed(false);
9795             }
9796             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9797         }
9798         
9799         if(this.multiple){
9800             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9801         }
9802         
9803         if(this.removable && !this.editable && !this.tickable){
9804             var close = this.closeTriggerEl();
9805             
9806             if(close){
9807                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9808                 close.on('click', this.removeBtnClick, this, close);
9809             }
9810         }
9811         
9812         //this.trigger.addClassOnOver('x-form-trigger-over');
9813         //this.trigger.addClassOnClick('x-form-trigger-click');
9814         
9815         //if(!this.width){
9816         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9817         //}
9818     },
9819     
9820     closeTriggerEl : function()
9821     {
9822         var close = this.el.select('.roo-combo-removable-btn', true).first();
9823         return close ? close : false;
9824     },
9825     
9826     removeBtnClick : function(e, h, el)
9827     {
9828         e.preventDefault();
9829         
9830         if(this.fireEvent("remove", this) !== false){
9831             this.reset();
9832             this.fireEvent("afterremove", this)
9833         }
9834     },
9835     
9836     createList : function()
9837     {
9838         this.list = Roo.get(document.body).createChild({
9839             tag: 'ul',
9840             cls: 'typeahead typeahead-long dropdown-menu',
9841             style: 'display:none'
9842         });
9843         
9844         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9845         
9846     },
9847
9848     // private
9849     initTrigger : function(){
9850        
9851     },
9852
9853     // private
9854     onDestroy : function(){
9855         if(this.trigger){
9856             this.trigger.removeAllListeners();
9857           //  this.trigger.remove();
9858         }
9859         //if(this.wrap){
9860         //    this.wrap.remove();
9861         //}
9862         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9863     },
9864
9865     // private
9866     onFocus : function(){
9867         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9868         /*
9869         if(!this.mimicing){
9870             this.wrap.addClass('x-trigger-wrap-focus');
9871             this.mimicing = true;
9872             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9873             if(this.monitorTab){
9874                 this.el.on("keydown", this.checkTab, this);
9875             }
9876         }
9877         */
9878     },
9879
9880     // private
9881     checkTab : function(e){
9882         if(e.getKey() == e.TAB){
9883             this.triggerBlur();
9884         }
9885     },
9886
9887     // private
9888     onBlur : function(){
9889         // do nothing
9890     },
9891
9892     // private
9893     mimicBlur : function(e, t){
9894         /*
9895         if(!this.wrap.contains(t) && this.validateBlur()){
9896             this.triggerBlur();
9897         }
9898         */
9899     },
9900
9901     // private
9902     triggerBlur : function(){
9903         this.mimicing = false;
9904         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9905         if(this.monitorTab){
9906             this.el.un("keydown", this.checkTab, this);
9907         }
9908         //this.wrap.removeClass('x-trigger-wrap-focus');
9909         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9910     },
9911
9912     // private
9913     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9914     validateBlur : function(e, t){
9915         return true;
9916     },
9917
9918     // private
9919     onDisable : function(){
9920         this.inputEl().dom.disabled = true;
9921         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9922         //if(this.wrap){
9923         //    this.wrap.addClass('x-item-disabled');
9924         //}
9925     },
9926
9927     // private
9928     onEnable : function(){
9929         this.inputEl().dom.disabled = false;
9930         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9931         //if(this.wrap){
9932         //    this.el.removeClass('x-item-disabled');
9933         //}
9934     },
9935
9936     // private
9937     onShow : function(){
9938         var ae = this.getActionEl();
9939         
9940         if(ae){
9941             ae.dom.style.display = '';
9942             ae.dom.style.visibility = 'visible';
9943         }
9944     },
9945
9946     // private
9947     
9948     onHide : function(){
9949         var ae = this.getActionEl();
9950         ae.dom.style.display = 'none';
9951     },
9952
9953     /**
9954      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9955      * by an implementing function.
9956      * @method
9957      * @param {EventObject} e
9958      */
9959     onTriggerClick : Roo.emptyFn
9960 });
9961  /*
9962  * Based on:
9963  * Ext JS Library 1.1.1
9964  * Copyright(c) 2006-2007, Ext JS, LLC.
9965  *
9966  * Originally Released Under LGPL - original licence link has changed is not relivant.
9967  *
9968  * Fork - LGPL
9969  * <script type="text/javascript">
9970  */
9971
9972
9973 /**
9974  * @class Roo.data.SortTypes
9975  * @singleton
9976  * Defines the default sorting (casting?) comparison functions used when sorting data.
9977  */
9978 Roo.data.SortTypes = {
9979     /**
9980      * Default sort that does nothing
9981      * @param {Mixed} s The value being converted
9982      * @return {Mixed} The comparison value
9983      */
9984     none : function(s){
9985         return s;
9986     },
9987     
9988     /**
9989      * The regular expression used to strip tags
9990      * @type {RegExp}
9991      * @property
9992      */
9993     stripTagsRE : /<\/?[^>]+>/gi,
9994     
9995     /**
9996      * Strips all HTML tags to sort on text only
9997      * @param {Mixed} s The value being converted
9998      * @return {String} The comparison value
9999      */
10000     asText : function(s){
10001         return String(s).replace(this.stripTagsRE, "");
10002     },
10003     
10004     /**
10005      * Strips all HTML tags to sort on text only - Case insensitive
10006      * @param {Mixed} s The value being converted
10007      * @return {String} The comparison value
10008      */
10009     asUCText : function(s){
10010         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10011     },
10012     
10013     /**
10014      * Case insensitive string
10015      * @param {Mixed} s The value being converted
10016      * @return {String} The comparison value
10017      */
10018     asUCString : function(s) {
10019         return String(s).toUpperCase();
10020     },
10021     
10022     /**
10023      * Date sorting
10024      * @param {Mixed} s The value being converted
10025      * @return {Number} The comparison value
10026      */
10027     asDate : function(s) {
10028         if(!s){
10029             return 0;
10030         }
10031         if(s instanceof Date){
10032             return s.getTime();
10033         }
10034         return Date.parse(String(s));
10035     },
10036     
10037     /**
10038      * Float sorting
10039      * @param {Mixed} s The value being converted
10040      * @return {Float} The comparison value
10041      */
10042     asFloat : function(s) {
10043         var val = parseFloat(String(s).replace(/,/g, ""));
10044         if(isNaN(val)) {
10045             val = 0;
10046         }
10047         return val;
10048     },
10049     
10050     /**
10051      * Integer sorting
10052      * @param {Mixed} s The value being converted
10053      * @return {Number} The comparison value
10054      */
10055     asInt : function(s) {
10056         var val = parseInt(String(s).replace(/,/g, ""));
10057         if(isNaN(val)) {
10058             val = 0;
10059         }
10060         return val;
10061     }
10062 };/*
10063  * Based on:
10064  * Ext JS Library 1.1.1
10065  * Copyright(c) 2006-2007, Ext JS, LLC.
10066  *
10067  * Originally Released Under LGPL - original licence link has changed is not relivant.
10068  *
10069  * Fork - LGPL
10070  * <script type="text/javascript">
10071  */
10072
10073 /**
10074 * @class Roo.data.Record
10075  * Instances of this class encapsulate both record <em>definition</em> information, and record
10076  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10077  * to access Records cached in an {@link Roo.data.Store} object.<br>
10078  * <p>
10079  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10080  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10081  * objects.<br>
10082  * <p>
10083  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10084  * @constructor
10085  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10086  * {@link #create}. The parameters are the same.
10087  * @param {Array} data An associative Array of data values keyed by the field name.
10088  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10089  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10090  * not specified an integer id is generated.
10091  */
10092 Roo.data.Record = function(data, id){
10093     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10094     this.data = data;
10095 };
10096
10097 /**
10098  * Generate a constructor for a specific record layout.
10099  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10100  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10101  * Each field definition object may contain the following properties: <ul>
10102  * <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,
10103  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10104  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10105  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10106  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10107  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10108  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10109  * this may be omitted.</p></li>
10110  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10111  * <ul><li>auto (Default, implies no conversion)</li>
10112  * <li>string</li>
10113  * <li>int</li>
10114  * <li>float</li>
10115  * <li>boolean</li>
10116  * <li>date</li></ul></p></li>
10117  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10118  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10119  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10120  * by the Reader into an object that will be stored in the Record. It is passed the
10121  * following parameters:<ul>
10122  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10123  * </ul></p></li>
10124  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10125  * </ul>
10126  * <br>usage:<br><pre><code>
10127 var TopicRecord = Roo.data.Record.create(
10128     {name: 'title', mapping: 'topic_title'},
10129     {name: 'author', mapping: 'username'},
10130     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10131     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10132     {name: 'lastPoster', mapping: 'user2'},
10133     {name: 'excerpt', mapping: 'post_text'}
10134 );
10135
10136 var myNewRecord = new TopicRecord({
10137     title: 'Do my job please',
10138     author: 'noobie',
10139     totalPosts: 1,
10140     lastPost: new Date(),
10141     lastPoster: 'Animal',
10142     excerpt: 'No way dude!'
10143 });
10144 myStore.add(myNewRecord);
10145 </code></pre>
10146  * @method create
10147  * @static
10148  */
10149 Roo.data.Record.create = function(o){
10150     var f = function(){
10151         f.superclass.constructor.apply(this, arguments);
10152     };
10153     Roo.extend(f, Roo.data.Record);
10154     var p = f.prototype;
10155     p.fields = new Roo.util.MixedCollection(false, function(field){
10156         return field.name;
10157     });
10158     for(var i = 0, len = o.length; i < len; i++){
10159         p.fields.add(new Roo.data.Field(o[i]));
10160     }
10161     f.getField = function(name){
10162         return p.fields.get(name);  
10163     };
10164     return f;
10165 };
10166
10167 Roo.data.Record.AUTO_ID = 1000;
10168 Roo.data.Record.EDIT = 'edit';
10169 Roo.data.Record.REJECT = 'reject';
10170 Roo.data.Record.COMMIT = 'commit';
10171
10172 Roo.data.Record.prototype = {
10173     /**
10174      * Readonly flag - true if this record has been modified.
10175      * @type Boolean
10176      */
10177     dirty : false,
10178     editing : false,
10179     error: null,
10180     modified: null,
10181
10182     // private
10183     join : function(store){
10184         this.store = store;
10185     },
10186
10187     /**
10188      * Set the named field to the specified value.
10189      * @param {String} name The name of the field to set.
10190      * @param {Object} value The value to set the field to.
10191      */
10192     set : function(name, value){
10193         if(this.data[name] == value){
10194             return;
10195         }
10196         this.dirty = true;
10197         if(!this.modified){
10198             this.modified = {};
10199         }
10200         if(typeof this.modified[name] == 'undefined'){
10201             this.modified[name] = this.data[name];
10202         }
10203         this.data[name] = value;
10204         if(!this.editing && this.store){
10205             this.store.afterEdit(this);
10206         }       
10207     },
10208
10209     /**
10210      * Get the value of the named field.
10211      * @param {String} name The name of the field to get the value of.
10212      * @return {Object} The value of the field.
10213      */
10214     get : function(name){
10215         return this.data[name]; 
10216     },
10217
10218     // private
10219     beginEdit : function(){
10220         this.editing = true;
10221         this.modified = {}; 
10222     },
10223
10224     // private
10225     cancelEdit : function(){
10226         this.editing = false;
10227         delete this.modified;
10228     },
10229
10230     // private
10231     endEdit : function(){
10232         this.editing = false;
10233         if(this.dirty && this.store){
10234             this.store.afterEdit(this);
10235         }
10236     },
10237
10238     /**
10239      * Usually called by the {@link Roo.data.Store} which owns the Record.
10240      * Rejects all changes made to the Record since either creation, or the last commit operation.
10241      * Modified fields are reverted to their original values.
10242      * <p>
10243      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10244      * of reject operations.
10245      */
10246     reject : function(){
10247         var m = this.modified;
10248         for(var n in m){
10249             if(typeof m[n] != "function"){
10250                 this.data[n] = m[n];
10251             }
10252         }
10253         this.dirty = false;
10254         delete this.modified;
10255         this.editing = false;
10256         if(this.store){
10257             this.store.afterReject(this);
10258         }
10259     },
10260
10261     /**
10262      * Usually called by the {@link Roo.data.Store} which owns the Record.
10263      * Commits all changes made to the Record since either creation, or the last commit operation.
10264      * <p>
10265      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10266      * of commit operations.
10267      */
10268     commit : function(){
10269         this.dirty = false;
10270         delete this.modified;
10271         this.editing = false;
10272         if(this.store){
10273             this.store.afterCommit(this);
10274         }
10275     },
10276
10277     // private
10278     hasError : function(){
10279         return this.error != null;
10280     },
10281
10282     // private
10283     clearError : function(){
10284         this.error = null;
10285     },
10286
10287     /**
10288      * Creates a copy of this record.
10289      * @param {String} id (optional) A new record id if you don't want to use this record's id
10290      * @return {Record}
10291      */
10292     copy : function(newId) {
10293         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10294     }
10295 };/*
10296  * Based on:
10297  * Ext JS Library 1.1.1
10298  * Copyright(c) 2006-2007, Ext JS, LLC.
10299  *
10300  * Originally Released Under LGPL - original licence link has changed is not relivant.
10301  *
10302  * Fork - LGPL
10303  * <script type="text/javascript">
10304  */
10305
10306
10307
10308 /**
10309  * @class Roo.data.Store
10310  * @extends Roo.util.Observable
10311  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10312  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10313  * <p>
10314  * 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
10315  * has no knowledge of the format of the data returned by the Proxy.<br>
10316  * <p>
10317  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10318  * instances from the data object. These records are cached and made available through accessor functions.
10319  * @constructor
10320  * Creates a new Store.
10321  * @param {Object} config A config object containing the objects needed for the Store to access data,
10322  * and read the data into Records.
10323  */
10324 Roo.data.Store = function(config){
10325     this.data = new Roo.util.MixedCollection(false);
10326     this.data.getKey = function(o){
10327         return o.id;
10328     };
10329     this.baseParams = {};
10330     // private
10331     this.paramNames = {
10332         "start" : "start",
10333         "limit" : "limit",
10334         "sort" : "sort",
10335         "dir" : "dir",
10336         "multisort" : "_multisort"
10337     };
10338
10339     if(config && config.data){
10340         this.inlineData = config.data;
10341         delete config.data;
10342     }
10343
10344     Roo.apply(this, config);
10345     
10346     if(this.reader){ // reader passed
10347         this.reader = Roo.factory(this.reader, Roo.data);
10348         this.reader.xmodule = this.xmodule || false;
10349         if(!this.recordType){
10350             this.recordType = this.reader.recordType;
10351         }
10352         if(this.reader.onMetaChange){
10353             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10354         }
10355     }
10356
10357     if(this.recordType){
10358         this.fields = this.recordType.prototype.fields;
10359     }
10360     this.modified = [];
10361
10362     this.addEvents({
10363         /**
10364          * @event datachanged
10365          * Fires when the data cache has changed, and a widget which is using this Store
10366          * as a Record cache should refresh its view.
10367          * @param {Store} this
10368          */
10369         datachanged : true,
10370         /**
10371          * @event metachange
10372          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10373          * @param {Store} this
10374          * @param {Object} meta The JSON metadata
10375          */
10376         metachange : true,
10377         /**
10378          * @event add
10379          * Fires when Records have been added to the Store
10380          * @param {Store} this
10381          * @param {Roo.data.Record[]} records The array of Records added
10382          * @param {Number} index The index at which the record(s) were added
10383          */
10384         add : true,
10385         /**
10386          * @event remove
10387          * Fires when a Record has been removed from the Store
10388          * @param {Store} this
10389          * @param {Roo.data.Record} record The Record that was removed
10390          * @param {Number} index The index at which the record was removed
10391          */
10392         remove : true,
10393         /**
10394          * @event update
10395          * Fires when a Record has been updated
10396          * @param {Store} this
10397          * @param {Roo.data.Record} record The Record that was updated
10398          * @param {String} operation The update operation being performed.  Value may be one of:
10399          * <pre><code>
10400  Roo.data.Record.EDIT
10401  Roo.data.Record.REJECT
10402  Roo.data.Record.COMMIT
10403          * </code></pre>
10404          */
10405         update : true,
10406         /**
10407          * @event clear
10408          * Fires when the data cache has been cleared.
10409          * @param {Store} this
10410          */
10411         clear : true,
10412         /**
10413          * @event beforeload
10414          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10415          * the load action will be canceled.
10416          * @param {Store} this
10417          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10418          */
10419         beforeload : true,
10420         /**
10421          * @event beforeloadadd
10422          * Fires after a new set of Records has been loaded.
10423          * @param {Store} this
10424          * @param {Roo.data.Record[]} records The Records that were loaded
10425          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10426          */
10427         beforeloadadd : true,
10428         /**
10429          * @event load
10430          * Fires after a new set of Records has been loaded, before they are added to the store.
10431          * @param {Store} this
10432          * @param {Roo.data.Record[]} records The Records that were loaded
10433          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10434          * @params {Object} return from reader
10435          */
10436         load : true,
10437         /**
10438          * @event loadexception
10439          * Fires if an exception occurs in the Proxy during loading.
10440          * Called with the signature of the Proxy's "loadexception" event.
10441          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10442          * 
10443          * @param {Proxy} 
10444          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10445          * @param {Object} load options 
10446          * @param {Object} jsonData from your request (normally this contains the Exception)
10447          */
10448         loadexception : true
10449     });
10450     
10451     if(this.proxy){
10452         this.proxy = Roo.factory(this.proxy, Roo.data);
10453         this.proxy.xmodule = this.xmodule || false;
10454         this.relayEvents(this.proxy,  ["loadexception"]);
10455     }
10456     this.sortToggle = {};
10457     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10458
10459     Roo.data.Store.superclass.constructor.call(this);
10460
10461     if(this.inlineData){
10462         this.loadData(this.inlineData);
10463         delete this.inlineData;
10464     }
10465 };
10466
10467 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10468      /**
10469     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10470     * without a remote query - used by combo/forms at present.
10471     */
10472     
10473     /**
10474     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10475     */
10476     /**
10477     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10478     */
10479     /**
10480     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10481     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10482     */
10483     /**
10484     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10485     * on any HTTP request
10486     */
10487     /**
10488     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10489     */
10490     /**
10491     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10492     */
10493     multiSort: false,
10494     /**
10495     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10496     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10497     */
10498     remoteSort : false,
10499
10500     /**
10501     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10502      * loaded or when a record is removed. (defaults to false).
10503     */
10504     pruneModifiedRecords : false,
10505
10506     // private
10507     lastOptions : null,
10508
10509     /**
10510      * Add Records to the Store and fires the add event.
10511      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10512      */
10513     add : function(records){
10514         records = [].concat(records);
10515         for(var i = 0, len = records.length; i < len; i++){
10516             records[i].join(this);
10517         }
10518         var index = this.data.length;
10519         this.data.addAll(records);
10520         this.fireEvent("add", this, records, index);
10521     },
10522
10523     /**
10524      * Remove a Record from the Store and fires the remove event.
10525      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10526      */
10527     remove : function(record){
10528         var index = this.data.indexOf(record);
10529         this.data.removeAt(index);
10530         if(this.pruneModifiedRecords){
10531             this.modified.remove(record);
10532         }
10533         this.fireEvent("remove", this, record, index);
10534     },
10535
10536     /**
10537      * Remove all Records from the Store and fires the clear event.
10538      */
10539     removeAll : function(){
10540         this.data.clear();
10541         if(this.pruneModifiedRecords){
10542             this.modified = [];
10543         }
10544         this.fireEvent("clear", this);
10545     },
10546
10547     /**
10548      * Inserts Records to the Store at the given index and fires the add event.
10549      * @param {Number} index The start index at which to insert the passed Records.
10550      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10551      */
10552     insert : function(index, records){
10553         records = [].concat(records);
10554         for(var i = 0, len = records.length; i < len; i++){
10555             this.data.insert(index, records[i]);
10556             records[i].join(this);
10557         }
10558         this.fireEvent("add", this, records, index);
10559     },
10560
10561     /**
10562      * Get the index within the cache of the passed Record.
10563      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10564      * @return {Number} The index of the passed Record. Returns -1 if not found.
10565      */
10566     indexOf : function(record){
10567         return this.data.indexOf(record);
10568     },
10569
10570     /**
10571      * Get the index within the cache of the Record with the passed id.
10572      * @param {String} id The id of the Record to find.
10573      * @return {Number} The index of the Record. Returns -1 if not found.
10574      */
10575     indexOfId : function(id){
10576         return this.data.indexOfKey(id);
10577     },
10578
10579     /**
10580      * Get the Record with the specified id.
10581      * @param {String} id The id of the Record to find.
10582      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10583      */
10584     getById : function(id){
10585         return this.data.key(id);
10586     },
10587
10588     /**
10589      * Get the Record at the specified index.
10590      * @param {Number} index The index of the Record to find.
10591      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10592      */
10593     getAt : function(index){
10594         return this.data.itemAt(index);
10595     },
10596
10597     /**
10598      * Returns a range of Records between specified indices.
10599      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10600      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10601      * @return {Roo.data.Record[]} An array of Records
10602      */
10603     getRange : function(start, end){
10604         return this.data.getRange(start, end);
10605     },
10606
10607     // private
10608     storeOptions : function(o){
10609         o = Roo.apply({}, o);
10610         delete o.callback;
10611         delete o.scope;
10612         this.lastOptions = o;
10613     },
10614
10615     /**
10616      * Loads the Record cache from the configured Proxy using the configured Reader.
10617      * <p>
10618      * If using remote paging, then the first load call must specify the <em>start</em>
10619      * and <em>limit</em> properties in the options.params property to establish the initial
10620      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10621      * <p>
10622      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10623      * and this call will return before the new data has been loaded. Perform any post-processing
10624      * in a callback function, or in a "load" event handler.</strong>
10625      * <p>
10626      * @param {Object} options An object containing properties which control loading options:<ul>
10627      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10628      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10629      * passed the following arguments:<ul>
10630      * <li>r : Roo.data.Record[]</li>
10631      * <li>options: Options object from the load call</li>
10632      * <li>success: Boolean success indicator</li></ul></li>
10633      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10634      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10635      * </ul>
10636      */
10637     load : function(options){
10638         options = options || {};
10639         if(this.fireEvent("beforeload", this, options) !== false){
10640             this.storeOptions(options);
10641             var p = Roo.apply(options.params || {}, this.baseParams);
10642             // if meta was not loaded from remote source.. try requesting it.
10643             if (!this.reader.metaFromRemote) {
10644                 p._requestMeta = 1;
10645             }
10646             if(this.sortInfo && this.remoteSort){
10647                 var pn = this.paramNames;
10648                 p[pn["sort"]] = this.sortInfo.field;
10649                 p[pn["dir"]] = this.sortInfo.direction;
10650             }
10651             if (this.multiSort) {
10652                 var pn = this.paramNames;
10653                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10654             }
10655             
10656             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10657         }
10658     },
10659
10660     /**
10661      * Reloads the Record cache from the configured Proxy using the configured Reader and
10662      * the options from the last load operation performed.
10663      * @param {Object} options (optional) An object containing properties which may override the options
10664      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10665      * the most recently used options are reused).
10666      */
10667     reload : function(options){
10668         this.load(Roo.applyIf(options||{}, this.lastOptions));
10669     },
10670
10671     // private
10672     // Called as a callback by the Reader during a load operation.
10673     loadRecords : function(o, options, success){
10674         if(!o || success === false){
10675             if(success !== false){
10676                 this.fireEvent("load", this, [], options, o);
10677             }
10678             if(options.callback){
10679                 options.callback.call(options.scope || this, [], options, false);
10680             }
10681             return;
10682         }
10683         // if data returned failure - throw an exception.
10684         if (o.success === false) {
10685             // show a message if no listener is registered.
10686             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10687                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10688             }
10689             // loadmask wil be hooked into this..
10690             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10691             return;
10692         }
10693         var r = o.records, t = o.totalRecords || r.length;
10694         
10695         this.fireEvent("beforeloadadd", this, r, options, o);
10696         
10697         if(!options || options.add !== true){
10698             if(this.pruneModifiedRecords){
10699                 this.modified = [];
10700             }
10701             for(var i = 0, len = r.length; i < len; i++){
10702                 r[i].join(this);
10703             }
10704             if(this.snapshot){
10705                 this.data = this.snapshot;
10706                 delete this.snapshot;
10707             }
10708             this.data.clear();
10709             this.data.addAll(r);
10710             this.totalLength = t;
10711             this.applySort();
10712             this.fireEvent("datachanged", this);
10713         }else{
10714             this.totalLength = Math.max(t, this.data.length+r.length);
10715             this.add(r);
10716         }
10717         this.fireEvent("load", this, r, options, o);
10718         if(options.callback){
10719             options.callback.call(options.scope || this, r, options, true);
10720         }
10721     },
10722
10723
10724     /**
10725      * Loads data from a passed data block. A Reader which understands the format of the data
10726      * must have been configured in the constructor.
10727      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10728      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10729      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10730      */
10731     loadData : function(o, append){
10732         var r = this.reader.readRecords(o);
10733         this.loadRecords(r, {add: append}, true);
10734     },
10735
10736     /**
10737      * Gets the number of cached records.
10738      * <p>
10739      * <em>If using paging, this may not be the total size of the dataset. If the data object
10740      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10741      * the data set size</em>
10742      */
10743     getCount : function(){
10744         return this.data.length || 0;
10745     },
10746
10747     /**
10748      * Gets the total number of records in the dataset as returned by the server.
10749      * <p>
10750      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10751      * the dataset size</em>
10752      */
10753     getTotalCount : function(){
10754         return this.totalLength || 0;
10755     },
10756
10757     /**
10758      * Returns the sort state of the Store as an object with two properties:
10759      * <pre><code>
10760  field {String} The name of the field by which the Records are sorted
10761  direction {String} The sort order, "ASC" or "DESC"
10762      * </code></pre>
10763      */
10764     getSortState : function(){
10765         return this.sortInfo;
10766     },
10767
10768     // private
10769     applySort : function(){
10770         if(this.sortInfo && !this.remoteSort){
10771             var s = this.sortInfo, f = s.field;
10772             var st = this.fields.get(f).sortType;
10773             var fn = function(r1, r2){
10774                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10775                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10776             };
10777             this.data.sort(s.direction, fn);
10778             if(this.snapshot && this.snapshot != this.data){
10779                 this.snapshot.sort(s.direction, fn);
10780             }
10781         }
10782     },
10783
10784     /**
10785      * Sets the default sort column and order to be used by the next load operation.
10786      * @param {String} fieldName The name of the field to sort by.
10787      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10788      */
10789     setDefaultSort : function(field, dir){
10790         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10791     },
10792
10793     /**
10794      * Sort the Records.
10795      * If remote sorting is used, the sort is performed on the server, and the cache is
10796      * reloaded. If local sorting is used, the cache is sorted internally.
10797      * @param {String} fieldName The name of the field to sort by.
10798      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10799      */
10800     sort : function(fieldName, dir){
10801         var f = this.fields.get(fieldName);
10802         if(!dir){
10803             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10804             
10805             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10806                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10807             }else{
10808                 dir = f.sortDir;
10809             }
10810         }
10811         this.sortToggle[f.name] = dir;
10812         this.sortInfo = {field: f.name, direction: dir};
10813         if(!this.remoteSort){
10814             this.applySort();
10815             this.fireEvent("datachanged", this);
10816         }else{
10817             this.load(this.lastOptions);
10818         }
10819     },
10820
10821     /**
10822      * Calls the specified function for each of the Records in the cache.
10823      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10824      * Returning <em>false</em> aborts and exits the iteration.
10825      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10826      */
10827     each : function(fn, scope){
10828         this.data.each(fn, scope);
10829     },
10830
10831     /**
10832      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10833      * (e.g., during paging).
10834      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10835      */
10836     getModifiedRecords : function(){
10837         return this.modified;
10838     },
10839
10840     // private
10841     createFilterFn : function(property, value, anyMatch){
10842         if(!value.exec){ // not a regex
10843             value = String(value);
10844             if(value.length == 0){
10845                 return false;
10846             }
10847             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10848         }
10849         return function(r){
10850             return value.test(r.data[property]);
10851         };
10852     },
10853
10854     /**
10855      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10856      * @param {String} property A field on your records
10857      * @param {Number} start The record index to start at (defaults to 0)
10858      * @param {Number} end The last record index to include (defaults to length - 1)
10859      * @return {Number} The sum
10860      */
10861     sum : function(property, start, end){
10862         var rs = this.data.items, v = 0;
10863         start = start || 0;
10864         end = (end || end === 0) ? end : rs.length-1;
10865
10866         for(var i = start; i <= end; i++){
10867             v += (rs[i].data[property] || 0);
10868         }
10869         return v;
10870     },
10871
10872     /**
10873      * Filter the records by a specified property.
10874      * @param {String} field A field on your records
10875      * @param {String/RegExp} value Either a string that the field
10876      * should start with or a RegExp to test against the field
10877      * @param {Boolean} anyMatch True to match any part not just the beginning
10878      */
10879     filter : function(property, value, anyMatch){
10880         var fn = this.createFilterFn(property, value, anyMatch);
10881         return fn ? this.filterBy(fn) : this.clearFilter();
10882     },
10883
10884     /**
10885      * Filter by a function. The specified function will be called with each
10886      * record in this data source. If the function returns true the record is included,
10887      * otherwise it is filtered.
10888      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10889      * @param {Object} scope (optional) The scope of the function (defaults to this)
10890      */
10891     filterBy : function(fn, scope){
10892         this.snapshot = this.snapshot || this.data;
10893         this.data = this.queryBy(fn, scope||this);
10894         this.fireEvent("datachanged", this);
10895     },
10896
10897     /**
10898      * Query the records by a specified property.
10899      * @param {String} field A field on your records
10900      * @param {String/RegExp} value Either a string that the field
10901      * should start with or a RegExp to test against the field
10902      * @param {Boolean} anyMatch True to match any part not just the beginning
10903      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10904      */
10905     query : function(property, value, anyMatch){
10906         var fn = this.createFilterFn(property, value, anyMatch);
10907         return fn ? this.queryBy(fn) : this.data.clone();
10908     },
10909
10910     /**
10911      * Query by a function. The specified function will be called with each
10912      * record in this data source. If the function returns true the record is included
10913      * in the results.
10914      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10915      * @param {Object} scope (optional) The scope of the function (defaults to this)
10916       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10917      **/
10918     queryBy : function(fn, scope){
10919         var data = this.snapshot || this.data;
10920         return data.filterBy(fn, scope||this);
10921     },
10922
10923     /**
10924      * Collects unique values for a particular dataIndex from this store.
10925      * @param {String} dataIndex The property to collect
10926      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10927      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10928      * @return {Array} An array of the unique values
10929      **/
10930     collect : function(dataIndex, allowNull, bypassFilter){
10931         var d = (bypassFilter === true && this.snapshot) ?
10932                 this.snapshot.items : this.data.items;
10933         var v, sv, r = [], l = {};
10934         for(var i = 0, len = d.length; i < len; i++){
10935             v = d[i].data[dataIndex];
10936             sv = String(v);
10937             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10938                 l[sv] = true;
10939                 r[r.length] = v;
10940             }
10941         }
10942         return r;
10943     },
10944
10945     /**
10946      * Revert to a view of the Record cache with no filtering applied.
10947      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10948      */
10949     clearFilter : function(suppressEvent){
10950         if(this.snapshot && this.snapshot != this.data){
10951             this.data = this.snapshot;
10952             delete this.snapshot;
10953             if(suppressEvent !== true){
10954                 this.fireEvent("datachanged", this);
10955             }
10956         }
10957     },
10958
10959     // private
10960     afterEdit : function(record){
10961         if(this.modified.indexOf(record) == -1){
10962             this.modified.push(record);
10963         }
10964         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10965     },
10966     
10967     // private
10968     afterReject : function(record){
10969         this.modified.remove(record);
10970         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10971     },
10972
10973     // private
10974     afterCommit : function(record){
10975         this.modified.remove(record);
10976         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10977     },
10978
10979     /**
10980      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10981      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10982      */
10983     commitChanges : function(){
10984         var m = this.modified.slice(0);
10985         this.modified = [];
10986         for(var i = 0, len = m.length; i < len; i++){
10987             m[i].commit();
10988         }
10989     },
10990
10991     /**
10992      * Cancel outstanding changes on all changed records.
10993      */
10994     rejectChanges : function(){
10995         var m = this.modified.slice(0);
10996         this.modified = [];
10997         for(var i = 0, len = m.length; i < len; i++){
10998             m[i].reject();
10999         }
11000     },
11001
11002     onMetaChange : function(meta, rtype, o){
11003         this.recordType = rtype;
11004         this.fields = rtype.prototype.fields;
11005         delete this.snapshot;
11006         this.sortInfo = meta.sortInfo || this.sortInfo;
11007         this.modified = [];
11008         this.fireEvent('metachange', this, this.reader.meta);
11009     },
11010     
11011     moveIndex : function(data, type)
11012     {
11013         var index = this.indexOf(data);
11014         
11015         var newIndex = index + type;
11016         
11017         this.remove(data);
11018         
11019         this.insert(newIndex, data);
11020         
11021     }
11022 });/*
11023  * Based on:
11024  * Ext JS Library 1.1.1
11025  * Copyright(c) 2006-2007, Ext JS, LLC.
11026  *
11027  * Originally Released Under LGPL - original licence link has changed is not relivant.
11028  *
11029  * Fork - LGPL
11030  * <script type="text/javascript">
11031  */
11032
11033 /**
11034  * @class Roo.data.SimpleStore
11035  * @extends Roo.data.Store
11036  * Small helper class to make creating Stores from Array data easier.
11037  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11038  * @cfg {Array} fields An array of field definition objects, or field name strings.
11039  * @cfg {Array} data The multi-dimensional array of data
11040  * @constructor
11041  * @param {Object} config
11042  */
11043 Roo.data.SimpleStore = function(config){
11044     Roo.data.SimpleStore.superclass.constructor.call(this, {
11045         isLocal : true,
11046         reader: new Roo.data.ArrayReader({
11047                 id: config.id
11048             },
11049             Roo.data.Record.create(config.fields)
11050         ),
11051         proxy : new Roo.data.MemoryProxy(config.data)
11052     });
11053     this.load();
11054 };
11055 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11056  * Based on:
11057  * Ext JS Library 1.1.1
11058  * Copyright(c) 2006-2007, Ext JS, LLC.
11059  *
11060  * Originally Released Under LGPL - original licence link has changed is not relivant.
11061  *
11062  * Fork - LGPL
11063  * <script type="text/javascript">
11064  */
11065
11066 /**
11067 /**
11068  * @extends Roo.data.Store
11069  * @class Roo.data.JsonStore
11070  * Small helper class to make creating Stores for JSON data easier. <br/>
11071 <pre><code>
11072 var store = new Roo.data.JsonStore({
11073     url: 'get-images.php',
11074     root: 'images',
11075     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11076 });
11077 </code></pre>
11078  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11079  * JsonReader and HttpProxy (unless inline data is provided).</b>
11080  * @cfg {Array} fields An array of field definition objects, or field name strings.
11081  * @constructor
11082  * @param {Object} config
11083  */
11084 Roo.data.JsonStore = function(c){
11085     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11086         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11087         reader: new Roo.data.JsonReader(c, c.fields)
11088     }));
11089 };
11090 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11091  * Based on:
11092  * Ext JS Library 1.1.1
11093  * Copyright(c) 2006-2007, Ext JS, LLC.
11094  *
11095  * Originally Released Under LGPL - original licence link has changed is not relivant.
11096  *
11097  * Fork - LGPL
11098  * <script type="text/javascript">
11099  */
11100
11101  
11102 Roo.data.Field = function(config){
11103     if(typeof config == "string"){
11104         config = {name: config};
11105     }
11106     Roo.apply(this, config);
11107     
11108     if(!this.type){
11109         this.type = "auto";
11110     }
11111     
11112     var st = Roo.data.SortTypes;
11113     // named sortTypes are supported, here we look them up
11114     if(typeof this.sortType == "string"){
11115         this.sortType = st[this.sortType];
11116     }
11117     
11118     // set default sortType for strings and dates
11119     if(!this.sortType){
11120         switch(this.type){
11121             case "string":
11122                 this.sortType = st.asUCString;
11123                 break;
11124             case "date":
11125                 this.sortType = st.asDate;
11126                 break;
11127             default:
11128                 this.sortType = st.none;
11129         }
11130     }
11131
11132     // define once
11133     var stripRe = /[\$,%]/g;
11134
11135     // prebuilt conversion function for this field, instead of
11136     // switching every time we're reading a value
11137     if(!this.convert){
11138         var cv, dateFormat = this.dateFormat;
11139         switch(this.type){
11140             case "":
11141             case "auto":
11142             case undefined:
11143                 cv = function(v){ return v; };
11144                 break;
11145             case "string":
11146                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11147                 break;
11148             case "int":
11149                 cv = function(v){
11150                     return v !== undefined && v !== null && v !== '' ?
11151                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11152                     };
11153                 break;
11154             case "float":
11155                 cv = function(v){
11156                     return v !== undefined && v !== null && v !== '' ?
11157                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11158                     };
11159                 break;
11160             case "bool":
11161             case "boolean":
11162                 cv = function(v){ return v === true || v === "true" || v == 1; };
11163                 break;
11164             case "date":
11165                 cv = function(v){
11166                     if(!v){
11167                         return '';
11168                     }
11169                     if(v instanceof Date){
11170                         return v;
11171                     }
11172                     if(dateFormat){
11173                         if(dateFormat == "timestamp"){
11174                             return new Date(v*1000);
11175                         }
11176                         return Date.parseDate(v, dateFormat);
11177                     }
11178                     var parsed = Date.parse(v);
11179                     return parsed ? new Date(parsed) : null;
11180                 };
11181              break;
11182             
11183         }
11184         this.convert = cv;
11185     }
11186 };
11187
11188 Roo.data.Field.prototype = {
11189     dateFormat: null,
11190     defaultValue: "",
11191     mapping: null,
11192     sortType : null,
11193     sortDir : "ASC"
11194 };/*
11195  * Based on:
11196  * Ext JS Library 1.1.1
11197  * Copyright(c) 2006-2007, Ext JS, LLC.
11198  *
11199  * Originally Released Under LGPL - original licence link has changed is not relivant.
11200  *
11201  * Fork - LGPL
11202  * <script type="text/javascript">
11203  */
11204  
11205 // Base class for reading structured data from a data source.  This class is intended to be
11206 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11207
11208 /**
11209  * @class Roo.data.DataReader
11210  * Base class for reading structured data from a data source.  This class is intended to be
11211  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11212  */
11213
11214 Roo.data.DataReader = function(meta, recordType){
11215     
11216     this.meta = meta;
11217     
11218     this.recordType = recordType instanceof Array ? 
11219         Roo.data.Record.create(recordType) : recordType;
11220 };
11221
11222 Roo.data.DataReader.prototype = {
11223      /**
11224      * Create an empty record
11225      * @param {Object} data (optional) - overlay some values
11226      * @return {Roo.data.Record} record created.
11227      */
11228     newRow :  function(d) {
11229         var da =  {};
11230         this.recordType.prototype.fields.each(function(c) {
11231             switch( c.type) {
11232                 case 'int' : da[c.name] = 0; break;
11233                 case 'date' : da[c.name] = new Date(); break;
11234                 case 'float' : da[c.name] = 0.0; break;
11235                 case 'boolean' : da[c.name] = false; break;
11236                 default : da[c.name] = ""; break;
11237             }
11238             
11239         });
11240         return new this.recordType(Roo.apply(da, d));
11241     }
11242     
11243 };/*
11244  * Based on:
11245  * Ext JS Library 1.1.1
11246  * Copyright(c) 2006-2007, Ext JS, LLC.
11247  *
11248  * Originally Released Under LGPL - original licence link has changed is not relivant.
11249  *
11250  * Fork - LGPL
11251  * <script type="text/javascript">
11252  */
11253
11254 /**
11255  * @class Roo.data.DataProxy
11256  * @extends Roo.data.Observable
11257  * This class is an abstract base class for implementations which provide retrieval of
11258  * unformatted data objects.<br>
11259  * <p>
11260  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11261  * (of the appropriate type which knows how to parse the data object) to provide a block of
11262  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11263  * <p>
11264  * Custom implementations must implement the load method as described in
11265  * {@link Roo.data.HttpProxy#load}.
11266  */
11267 Roo.data.DataProxy = function(){
11268     this.addEvents({
11269         /**
11270          * @event beforeload
11271          * Fires before a network request is made to retrieve a data object.
11272          * @param {Object} This DataProxy object.
11273          * @param {Object} params The params parameter to the load function.
11274          */
11275         beforeload : true,
11276         /**
11277          * @event load
11278          * Fires before the load method's callback is called.
11279          * @param {Object} This DataProxy object.
11280          * @param {Object} o The data object.
11281          * @param {Object} arg The callback argument object passed to the load function.
11282          */
11283         load : true,
11284         /**
11285          * @event loadexception
11286          * Fires if an Exception occurs during data retrieval.
11287          * @param {Object} This DataProxy object.
11288          * @param {Object} o The data object.
11289          * @param {Object} arg The callback argument object passed to the load function.
11290          * @param {Object} e The Exception.
11291          */
11292         loadexception : true
11293     });
11294     Roo.data.DataProxy.superclass.constructor.call(this);
11295 };
11296
11297 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11298
11299     /**
11300      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11301      */
11302 /*
11303  * Based on:
11304  * Ext JS Library 1.1.1
11305  * Copyright(c) 2006-2007, Ext JS, LLC.
11306  *
11307  * Originally Released Under LGPL - original licence link has changed is not relivant.
11308  *
11309  * Fork - LGPL
11310  * <script type="text/javascript">
11311  */
11312 /**
11313  * @class Roo.data.MemoryProxy
11314  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11315  * to the Reader when its load method is called.
11316  * @constructor
11317  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11318  */
11319 Roo.data.MemoryProxy = function(data){
11320     if (data.data) {
11321         data = data.data;
11322     }
11323     Roo.data.MemoryProxy.superclass.constructor.call(this);
11324     this.data = data;
11325 };
11326
11327 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11328     
11329     /**
11330      * Load data from the requested source (in this case an in-memory
11331      * data object passed to the constructor), read the data object into
11332      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11333      * process that block using the passed callback.
11334      * @param {Object} params This parameter is not used by the MemoryProxy class.
11335      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11336      * object into a block of Roo.data.Records.
11337      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11338      * The function must be passed <ul>
11339      * <li>The Record block object</li>
11340      * <li>The "arg" argument from the load function</li>
11341      * <li>A boolean success indicator</li>
11342      * </ul>
11343      * @param {Object} scope The scope in which to call the callback
11344      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11345      */
11346     load : function(params, reader, callback, scope, arg){
11347         params = params || {};
11348         var result;
11349         try {
11350             result = reader.readRecords(this.data);
11351         }catch(e){
11352             this.fireEvent("loadexception", this, arg, null, e);
11353             callback.call(scope, null, arg, false);
11354             return;
11355         }
11356         callback.call(scope, result, arg, true);
11357     },
11358     
11359     // private
11360     update : function(params, records){
11361         
11362     }
11363 });/*
11364  * Based on:
11365  * Ext JS Library 1.1.1
11366  * Copyright(c) 2006-2007, Ext JS, LLC.
11367  *
11368  * Originally Released Under LGPL - original licence link has changed is not relivant.
11369  *
11370  * Fork - LGPL
11371  * <script type="text/javascript">
11372  */
11373 /**
11374  * @class Roo.data.HttpProxy
11375  * @extends Roo.data.DataProxy
11376  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11377  * configured to reference a certain URL.<br><br>
11378  * <p>
11379  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11380  * from which the running page was served.<br><br>
11381  * <p>
11382  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11383  * <p>
11384  * Be aware that to enable the browser to parse an XML document, the server must set
11385  * the Content-Type header in the HTTP response to "text/xml".
11386  * @constructor
11387  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11388  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11389  * will be used to make the request.
11390  */
11391 Roo.data.HttpProxy = function(conn){
11392     Roo.data.HttpProxy.superclass.constructor.call(this);
11393     // is conn a conn config or a real conn?
11394     this.conn = conn;
11395     this.useAjax = !conn || !conn.events;
11396   
11397 };
11398
11399 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11400     // thse are take from connection...
11401     
11402     /**
11403      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11404      */
11405     /**
11406      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11407      * extra parameters to each request made by this object. (defaults to undefined)
11408      */
11409     /**
11410      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11411      *  to each request made by this object. (defaults to undefined)
11412      */
11413     /**
11414      * @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)
11415      */
11416     /**
11417      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11418      */
11419      /**
11420      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11421      * @type Boolean
11422      */
11423   
11424
11425     /**
11426      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11427      * @type Boolean
11428      */
11429     /**
11430      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11431      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11432      * a finer-grained basis than the DataProxy events.
11433      */
11434     getConnection : function(){
11435         return this.useAjax ? Roo.Ajax : this.conn;
11436     },
11437
11438     /**
11439      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11440      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11441      * process that block using the passed callback.
11442      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11443      * for the request to the remote server.
11444      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11445      * object into a block of Roo.data.Records.
11446      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11447      * The function must be passed <ul>
11448      * <li>The Record block object</li>
11449      * <li>The "arg" argument from the load function</li>
11450      * <li>A boolean success indicator</li>
11451      * </ul>
11452      * @param {Object} scope The scope in which to call the callback
11453      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11454      */
11455     load : function(params, reader, callback, scope, arg){
11456         if(this.fireEvent("beforeload", this, params) !== false){
11457             var  o = {
11458                 params : params || {},
11459                 request: {
11460                     callback : callback,
11461                     scope : scope,
11462                     arg : arg
11463                 },
11464                 reader: reader,
11465                 callback : this.loadResponse,
11466                 scope: this
11467             };
11468             if(this.useAjax){
11469                 Roo.applyIf(o, this.conn);
11470                 if(this.activeRequest){
11471                     Roo.Ajax.abort(this.activeRequest);
11472                 }
11473                 this.activeRequest = Roo.Ajax.request(o);
11474             }else{
11475                 this.conn.request(o);
11476             }
11477         }else{
11478             callback.call(scope||this, null, arg, false);
11479         }
11480     },
11481
11482     // private
11483     loadResponse : function(o, success, response){
11484         delete this.activeRequest;
11485         if(!success){
11486             this.fireEvent("loadexception", this, o, response);
11487             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11488             return;
11489         }
11490         var result;
11491         try {
11492             result = o.reader.read(response);
11493         }catch(e){
11494             this.fireEvent("loadexception", this, o, response, e);
11495             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11496             return;
11497         }
11498         
11499         this.fireEvent("load", this, o, o.request.arg);
11500         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11501     },
11502
11503     // private
11504     update : function(dataSet){
11505
11506     },
11507
11508     // private
11509     updateResponse : function(dataSet){
11510
11511     }
11512 });/*
11513  * Based on:
11514  * Ext JS Library 1.1.1
11515  * Copyright(c) 2006-2007, Ext JS, LLC.
11516  *
11517  * Originally Released Under LGPL - original licence link has changed is not relivant.
11518  *
11519  * Fork - LGPL
11520  * <script type="text/javascript">
11521  */
11522
11523 /**
11524  * @class Roo.data.ScriptTagProxy
11525  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11526  * other than the originating domain of the running page.<br><br>
11527  * <p>
11528  * <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
11529  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11530  * <p>
11531  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11532  * source code that is used as the source inside a &lt;script> tag.<br><br>
11533  * <p>
11534  * In order for the browser to process the returned data, the server must wrap the data object
11535  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11536  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11537  * depending on whether the callback name was passed:
11538  * <p>
11539  * <pre><code>
11540 boolean scriptTag = false;
11541 String cb = request.getParameter("callback");
11542 if (cb != null) {
11543     scriptTag = true;
11544     response.setContentType("text/javascript");
11545 } else {
11546     response.setContentType("application/x-json");
11547 }
11548 Writer out = response.getWriter();
11549 if (scriptTag) {
11550     out.write(cb + "(");
11551 }
11552 out.print(dataBlock.toJsonString());
11553 if (scriptTag) {
11554     out.write(");");
11555 }
11556 </pre></code>
11557  *
11558  * @constructor
11559  * @param {Object} config A configuration object.
11560  */
11561 Roo.data.ScriptTagProxy = function(config){
11562     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11563     Roo.apply(this, config);
11564     this.head = document.getElementsByTagName("head")[0];
11565 };
11566
11567 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11568
11569 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11570     /**
11571      * @cfg {String} url The URL from which to request the data object.
11572      */
11573     /**
11574      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11575      */
11576     timeout : 30000,
11577     /**
11578      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11579      * the server the name of the callback function set up by the load call to process the returned data object.
11580      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11581      * javascript output which calls this named function passing the data object as its only parameter.
11582      */
11583     callbackParam : "callback",
11584     /**
11585      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11586      * name to the request.
11587      */
11588     nocache : true,
11589
11590     /**
11591      * Load data from the configured URL, read the data object into
11592      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11593      * process that block using the passed callback.
11594      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11595      * for the request to the remote server.
11596      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11597      * object into a block of Roo.data.Records.
11598      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11599      * The function must be passed <ul>
11600      * <li>The Record block object</li>
11601      * <li>The "arg" argument from the load function</li>
11602      * <li>A boolean success indicator</li>
11603      * </ul>
11604      * @param {Object} scope The scope in which to call the callback
11605      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11606      */
11607     load : function(params, reader, callback, scope, arg){
11608         if(this.fireEvent("beforeload", this, params) !== false){
11609
11610             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11611
11612             var url = this.url;
11613             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11614             if(this.nocache){
11615                 url += "&_dc=" + (new Date().getTime());
11616             }
11617             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11618             var trans = {
11619                 id : transId,
11620                 cb : "stcCallback"+transId,
11621                 scriptId : "stcScript"+transId,
11622                 params : params,
11623                 arg : arg,
11624                 url : url,
11625                 callback : callback,
11626                 scope : scope,
11627                 reader : reader
11628             };
11629             var conn = this;
11630
11631             window[trans.cb] = function(o){
11632                 conn.handleResponse(o, trans);
11633             };
11634
11635             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11636
11637             if(this.autoAbort !== false){
11638                 this.abort();
11639             }
11640
11641             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11642
11643             var script = document.createElement("script");
11644             script.setAttribute("src", url);
11645             script.setAttribute("type", "text/javascript");
11646             script.setAttribute("id", trans.scriptId);
11647             this.head.appendChild(script);
11648
11649             this.trans = trans;
11650         }else{
11651             callback.call(scope||this, null, arg, false);
11652         }
11653     },
11654
11655     // private
11656     isLoading : function(){
11657         return this.trans ? true : false;
11658     },
11659
11660     /**
11661      * Abort the current server request.
11662      */
11663     abort : function(){
11664         if(this.isLoading()){
11665             this.destroyTrans(this.trans);
11666         }
11667     },
11668
11669     // private
11670     destroyTrans : function(trans, isLoaded){
11671         this.head.removeChild(document.getElementById(trans.scriptId));
11672         clearTimeout(trans.timeoutId);
11673         if(isLoaded){
11674             window[trans.cb] = undefined;
11675             try{
11676                 delete window[trans.cb];
11677             }catch(e){}
11678         }else{
11679             // if hasn't been loaded, wait for load to remove it to prevent script error
11680             window[trans.cb] = function(){
11681                 window[trans.cb] = undefined;
11682                 try{
11683                     delete window[trans.cb];
11684                 }catch(e){}
11685             };
11686         }
11687     },
11688
11689     // private
11690     handleResponse : function(o, trans){
11691         this.trans = false;
11692         this.destroyTrans(trans, true);
11693         var result;
11694         try {
11695             result = trans.reader.readRecords(o);
11696         }catch(e){
11697             this.fireEvent("loadexception", this, o, trans.arg, e);
11698             trans.callback.call(trans.scope||window, null, trans.arg, false);
11699             return;
11700         }
11701         this.fireEvent("load", this, o, trans.arg);
11702         trans.callback.call(trans.scope||window, result, trans.arg, true);
11703     },
11704
11705     // private
11706     handleFailure : function(trans){
11707         this.trans = false;
11708         this.destroyTrans(trans, false);
11709         this.fireEvent("loadexception", this, null, trans.arg);
11710         trans.callback.call(trans.scope||window, null, trans.arg, false);
11711     }
11712 });/*
11713  * Based on:
11714  * Ext JS Library 1.1.1
11715  * Copyright(c) 2006-2007, Ext JS, LLC.
11716  *
11717  * Originally Released Under LGPL - original licence link has changed is not relivant.
11718  *
11719  * Fork - LGPL
11720  * <script type="text/javascript">
11721  */
11722
11723 /**
11724  * @class Roo.data.JsonReader
11725  * @extends Roo.data.DataReader
11726  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11727  * based on mappings in a provided Roo.data.Record constructor.
11728  * 
11729  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11730  * in the reply previously. 
11731  * 
11732  * <p>
11733  * Example code:
11734  * <pre><code>
11735 var RecordDef = Roo.data.Record.create([
11736     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11737     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11738 ]);
11739 var myReader = new Roo.data.JsonReader({
11740     totalProperty: "results",    // The property which contains the total dataset size (optional)
11741     root: "rows",                // The property which contains an Array of row objects
11742     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11743 }, RecordDef);
11744 </code></pre>
11745  * <p>
11746  * This would consume a JSON file like this:
11747  * <pre><code>
11748 { 'results': 2, 'rows': [
11749     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11750     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11751 }
11752 </code></pre>
11753  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11754  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11755  * paged from the remote server.
11756  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11757  * @cfg {String} root name of the property which contains the Array of row objects.
11758  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11759  * @cfg {Array} fields Array of field definition objects
11760  * @constructor
11761  * Create a new JsonReader
11762  * @param {Object} meta Metadata configuration options
11763  * @param {Object} recordType Either an Array of field definition objects,
11764  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11765  */
11766 Roo.data.JsonReader = function(meta, recordType){
11767     
11768     meta = meta || {};
11769     // set some defaults:
11770     Roo.applyIf(meta, {
11771         totalProperty: 'total',
11772         successProperty : 'success',
11773         root : 'data',
11774         id : 'id'
11775     });
11776     
11777     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11778 };
11779 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11780     
11781     /**
11782      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11783      * Used by Store query builder to append _requestMeta to params.
11784      * 
11785      */
11786     metaFromRemote : false,
11787     /**
11788      * This method is only used by a DataProxy which has retrieved data from a remote server.
11789      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11790      * @return {Object} data A data block which is used by an Roo.data.Store object as
11791      * a cache of Roo.data.Records.
11792      */
11793     read : function(response){
11794         var json = response.responseText;
11795        
11796         var o = /* eval:var:o */ eval("("+json+")");
11797         if(!o) {
11798             throw {message: "JsonReader.read: Json object not found"};
11799         }
11800         
11801         if(o.metaData){
11802             
11803             delete this.ef;
11804             this.metaFromRemote = true;
11805             this.meta = o.metaData;
11806             this.recordType = Roo.data.Record.create(o.metaData.fields);
11807             this.onMetaChange(this.meta, this.recordType, o);
11808         }
11809         return this.readRecords(o);
11810     },
11811
11812     // private function a store will implement
11813     onMetaChange : function(meta, recordType, o){
11814
11815     },
11816
11817     /**
11818          * @ignore
11819          */
11820     simpleAccess: function(obj, subsc) {
11821         return obj[subsc];
11822     },
11823
11824         /**
11825          * @ignore
11826          */
11827     getJsonAccessor: function(){
11828         var re = /[\[\.]/;
11829         return function(expr) {
11830             try {
11831                 return(re.test(expr))
11832                     ? new Function("obj", "return obj." + expr)
11833                     : function(obj){
11834                         return obj[expr];
11835                     };
11836             } catch(e){}
11837             return Roo.emptyFn;
11838         };
11839     }(),
11840
11841     /**
11842      * Create a data block containing Roo.data.Records from an XML document.
11843      * @param {Object} o An object which contains an Array of row objects in the property specified
11844      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11845      * which contains the total size of the dataset.
11846      * @return {Object} data A data block which is used by an Roo.data.Store object as
11847      * a cache of Roo.data.Records.
11848      */
11849     readRecords : function(o){
11850         /**
11851          * After any data loads, the raw JSON data is available for further custom processing.
11852          * @type Object
11853          */
11854         this.o = o;
11855         var s = this.meta, Record = this.recordType,
11856             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11857
11858 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11859         if (!this.ef) {
11860             if(s.totalProperty) {
11861                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11862                 }
11863                 if(s.successProperty) {
11864                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11865                 }
11866                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11867                 if (s.id) {
11868                         var g = this.getJsonAccessor(s.id);
11869                         this.getId = function(rec) {
11870                                 var r = g(rec);  
11871                                 return (r === undefined || r === "") ? null : r;
11872                         };
11873                 } else {
11874                         this.getId = function(){return null;};
11875                 }
11876             this.ef = [];
11877             for(var jj = 0; jj < fl; jj++){
11878                 f = fi[jj];
11879                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11880                 this.ef[jj] = this.getJsonAccessor(map);
11881             }
11882         }
11883
11884         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11885         if(s.totalProperty){
11886             var vt = parseInt(this.getTotal(o), 10);
11887             if(!isNaN(vt)){
11888                 totalRecords = vt;
11889             }
11890         }
11891         if(s.successProperty){
11892             var vs = this.getSuccess(o);
11893             if(vs === false || vs === 'false'){
11894                 success = false;
11895             }
11896         }
11897         var records = [];
11898         for(var i = 0; i < c; i++){
11899                 var n = root[i];
11900             var values = {};
11901             var id = this.getId(n);
11902             for(var j = 0; j < fl; j++){
11903                 f = fi[j];
11904             var v = this.ef[j](n);
11905             if (!f.convert) {
11906                 Roo.log('missing convert for ' + f.name);
11907                 Roo.log(f);
11908                 continue;
11909             }
11910             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11911             }
11912             var record = new Record(values, id);
11913             record.json = n;
11914             records[i] = record;
11915         }
11916         return {
11917             raw : o,
11918             success : success,
11919             records : records,
11920             totalRecords : totalRecords
11921         };
11922     }
11923 });/*
11924  * Based on:
11925  * Ext JS Library 1.1.1
11926  * Copyright(c) 2006-2007, Ext JS, LLC.
11927  *
11928  * Originally Released Under LGPL - original licence link has changed is not relivant.
11929  *
11930  * Fork - LGPL
11931  * <script type="text/javascript">
11932  */
11933
11934 /**
11935  * @class Roo.data.ArrayReader
11936  * @extends Roo.data.DataReader
11937  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11938  * Each element of that Array represents a row of data fields. The
11939  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11940  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11941  * <p>
11942  * Example code:.
11943  * <pre><code>
11944 var RecordDef = Roo.data.Record.create([
11945     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11946     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11947 ]);
11948 var myReader = new Roo.data.ArrayReader({
11949     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11950 }, RecordDef);
11951 </code></pre>
11952  * <p>
11953  * This would consume an Array like this:
11954  * <pre><code>
11955 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11956   </code></pre>
11957  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11958  * @constructor
11959  * Create a new JsonReader
11960  * @param {Object} meta Metadata configuration options.
11961  * @param {Object} recordType Either an Array of field definition objects
11962  * as specified to {@link Roo.data.Record#create},
11963  * or an {@link Roo.data.Record} object
11964  * created using {@link Roo.data.Record#create}.
11965  */
11966 Roo.data.ArrayReader = function(meta, recordType){
11967     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11968 };
11969
11970 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11971     /**
11972      * Create a data block containing Roo.data.Records from an XML document.
11973      * @param {Object} o An Array of row objects which represents the dataset.
11974      * @return {Object} data A data block which is used by an Roo.data.Store object as
11975      * a cache of Roo.data.Records.
11976      */
11977     readRecords : function(o){
11978         var sid = this.meta ? this.meta.id : null;
11979         var recordType = this.recordType, fields = recordType.prototype.fields;
11980         var records = [];
11981         var root = o;
11982             for(var i = 0; i < root.length; i++){
11983                     var n = root[i];
11984                 var values = {};
11985                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11986                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11987                 var f = fields.items[j];
11988                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11989                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11990                 v = f.convert(v);
11991                 values[f.name] = v;
11992             }
11993                 var record = new recordType(values, id);
11994                 record.json = n;
11995                 records[records.length] = record;
11996             }
11997             return {
11998                 records : records,
11999                 totalRecords : records.length
12000             };
12001     }
12002 });/*
12003  * - LGPL
12004  * * 
12005  */
12006
12007 /**
12008  * @class Roo.bootstrap.ComboBox
12009  * @extends Roo.bootstrap.TriggerField
12010  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12011  * @cfg {Boolean} append (true|false) default false
12012  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12013  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12014  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12015  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12016  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12017  * @cfg {Boolean} animate default true
12018  * @cfg {Boolean} emptyResultText only for touch device
12019  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12020  * @constructor
12021  * Create a new ComboBox.
12022  * @param {Object} config Configuration options
12023  */
12024 Roo.bootstrap.ComboBox = function(config){
12025     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12026     this.addEvents({
12027         /**
12028          * @event expand
12029          * Fires when the dropdown list is expanded
12030              * @param {Roo.bootstrap.ComboBox} combo This combo box
12031              */
12032         'expand' : true,
12033         /**
12034          * @event collapse
12035          * Fires when the dropdown list is collapsed
12036              * @param {Roo.bootstrap.ComboBox} combo This combo box
12037              */
12038         'collapse' : true,
12039         /**
12040          * @event beforeselect
12041          * Fires before a list item is selected. Return false to cancel the selection.
12042              * @param {Roo.bootstrap.ComboBox} combo This combo box
12043              * @param {Roo.data.Record} record The data record returned from the underlying store
12044              * @param {Number} index The index of the selected item in the dropdown list
12045              */
12046         'beforeselect' : true,
12047         /**
12048          * @event select
12049          * Fires when a list item is selected
12050              * @param {Roo.bootstrap.ComboBox} combo This combo box
12051              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12052              * @param {Number} index The index of the selected item in the dropdown list
12053              */
12054         'select' : true,
12055         /**
12056          * @event beforequery
12057          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12058          * The event object passed has these properties:
12059              * @param {Roo.bootstrap.ComboBox} combo This combo box
12060              * @param {String} query The query
12061              * @param {Boolean} forceAll true to force "all" query
12062              * @param {Boolean} cancel true to cancel the query
12063              * @param {Object} e The query event object
12064              */
12065         'beforequery': true,
12066          /**
12067          * @event add
12068          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12069              * @param {Roo.bootstrap.ComboBox} combo This combo box
12070              */
12071         'add' : true,
12072         /**
12073          * @event edit
12074          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12075              * @param {Roo.bootstrap.ComboBox} combo This combo box
12076              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12077              */
12078         'edit' : true,
12079         /**
12080          * @event remove
12081          * Fires when the remove value from the combobox array
12082              * @param {Roo.bootstrap.ComboBox} combo This combo box
12083              */
12084         'remove' : true,
12085         /**
12086          * @event afterremove
12087          * Fires when the remove value from the combobox array
12088              * @param {Roo.bootstrap.ComboBox} combo This combo box
12089              */
12090         'afterremove' : true,
12091         /**
12092          * @event specialfilter
12093          * Fires when specialfilter
12094             * @param {Roo.bootstrap.ComboBox} combo This combo box
12095             */
12096         'specialfilter' : true,
12097         /**
12098          * @event tick
12099          * Fires when tick the element
12100             * @param {Roo.bootstrap.ComboBox} combo This combo box
12101             */
12102         'tick' : true,
12103         /**
12104          * @event touchviewdisplay
12105          * Fires when touch view require special display (default is using displayField)
12106             * @param {Roo.bootstrap.ComboBox} combo This combo box
12107             * @param {Object} cfg set html .
12108             */
12109         'touchviewdisplay' : true
12110         
12111     });
12112     
12113     this.item = [];
12114     this.tickItems = [];
12115     
12116     this.selectedIndex = -1;
12117     if(this.mode == 'local'){
12118         if(config.queryDelay === undefined){
12119             this.queryDelay = 10;
12120         }
12121         if(config.minChars === undefined){
12122             this.minChars = 0;
12123         }
12124     }
12125 };
12126
12127 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12128      
12129     /**
12130      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12131      * rendering into an Roo.Editor, defaults to false)
12132      */
12133     /**
12134      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12135      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12136      */
12137     /**
12138      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12139      */
12140     /**
12141      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12142      * the dropdown list (defaults to undefined, with no header element)
12143      */
12144
12145      /**
12146      * @cfg {String/Roo.Template} tpl The template to use to render the output
12147      */
12148      
12149      /**
12150      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12151      */
12152     listWidth: undefined,
12153     /**
12154      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12155      * mode = 'remote' or 'text' if mode = 'local')
12156      */
12157     displayField: undefined,
12158     
12159     /**
12160      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12161      * mode = 'remote' or 'value' if mode = 'local'). 
12162      * Note: use of a valueField requires the user make a selection
12163      * in order for a value to be mapped.
12164      */
12165     valueField: undefined,
12166     /**
12167      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12168      */
12169     modalTitle : '',
12170     
12171     /**
12172      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12173      * field's data value (defaults to the underlying DOM element's name)
12174      */
12175     hiddenName: undefined,
12176     /**
12177      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12178      */
12179     listClass: '',
12180     /**
12181      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12182      */
12183     selectedClass: 'active',
12184     
12185     /**
12186      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12187      */
12188     shadow:'sides',
12189     /**
12190      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12191      * anchor positions (defaults to 'tl-bl')
12192      */
12193     listAlign: 'tl-bl?',
12194     /**
12195      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12196      */
12197     maxHeight: 300,
12198     /**
12199      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12200      * query specified by the allQuery config option (defaults to 'query')
12201      */
12202     triggerAction: 'query',
12203     /**
12204      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12205      * (defaults to 4, does not apply if editable = false)
12206      */
12207     minChars : 4,
12208     /**
12209      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12210      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12211      */
12212     typeAhead: false,
12213     /**
12214      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12215      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12216      */
12217     queryDelay: 500,
12218     /**
12219      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12220      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12221      */
12222     pageSize: 0,
12223     /**
12224      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12225      * when editable = true (defaults to false)
12226      */
12227     selectOnFocus:false,
12228     /**
12229      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12230      */
12231     queryParam: 'query',
12232     /**
12233      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12234      * when mode = 'remote' (defaults to 'Loading...')
12235      */
12236     loadingText: 'Loading...',
12237     /**
12238      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12239      */
12240     resizable: false,
12241     /**
12242      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12243      */
12244     handleHeight : 8,
12245     /**
12246      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12247      * traditional select (defaults to true)
12248      */
12249     editable: true,
12250     /**
12251      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12252      */
12253     allQuery: '',
12254     /**
12255      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12256      */
12257     mode: 'remote',
12258     /**
12259      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12260      * listWidth has a higher value)
12261      */
12262     minListWidth : 70,
12263     /**
12264      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12265      * allow the user to set arbitrary text into the field (defaults to false)
12266      */
12267     forceSelection:false,
12268     /**
12269      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12270      * if typeAhead = true (defaults to 250)
12271      */
12272     typeAheadDelay : 250,
12273     /**
12274      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12275      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12276      */
12277     valueNotFoundText : undefined,
12278     /**
12279      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12280      */
12281     blockFocus : false,
12282     
12283     /**
12284      * @cfg {Boolean} disableClear Disable showing of clear button.
12285      */
12286     disableClear : false,
12287     /**
12288      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12289      */
12290     alwaysQuery : false,
12291     
12292     /**
12293      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12294      */
12295     multiple : false,
12296     
12297     /**
12298      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12299      */
12300     invalidClass : "has-warning",
12301     
12302     /**
12303      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12304      */
12305     validClass : "has-success",
12306     
12307     /**
12308      * @cfg {Boolean} specialFilter (true|false) special filter default false
12309      */
12310     specialFilter : false,
12311     
12312     /**
12313      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12314      */
12315     mobileTouchView : true,
12316     
12317     /**
12318      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12319      */
12320     useNativeIOS : false,
12321     
12322     ios_options : false,
12323     
12324     //private
12325     addicon : false,
12326     editicon: false,
12327     
12328     page: 0,
12329     hasQuery: false,
12330     append: false,
12331     loadNext: false,
12332     autoFocus : true,
12333     tickable : false,
12334     btnPosition : 'right',
12335     triggerList : true,
12336     showToggleBtn : true,
12337     animate : true,
12338     emptyResultText: 'Empty',
12339     triggerText : 'Select',
12340     
12341     // element that contains real text value.. (when hidden is used..)
12342     
12343     getAutoCreate : function()
12344     {
12345         var cfg = false;
12346         
12347         /*
12348          * Render classic select for iso
12349          */
12350         
12351         if(Roo.isIOS && this.useNativeIOS){
12352             cfg = this.getAutoCreateNativeIOS();
12353             return cfg;
12354         }
12355         
12356         /*
12357          * Touch Devices
12358          */
12359         
12360         if(Roo.isTouch && this.mobileTouchView){
12361             cfg = this.getAutoCreateTouchView();
12362             return cfg;;
12363         }
12364         
12365         /*
12366          *  Normal ComboBox
12367          */
12368         if(!this.tickable){
12369             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12370             return cfg;
12371         }
12372         
12373         /*
12374          *  ComboBox with tickable selections
12375          */
12376              
12377         var align = this.labelAlign || this.parentLabelAlign();
12378         
12379         cfg = {
12380             cls : 'form-group roo-combobox-tickable' //input-group
12381         };
12382         
12383         var btn_text_select = '';
12384         var btn_text_done = '';
12385         var btn_text_cancel = '';
12386         
12387         if (this.btn_text_show) {
12388             btn_text_select = 'Select';
12389             btn_text_done = 'Done';
12390             btn_text_cancel = 'Cancel'; 
12391         }
12392         
12393         var buttons = {
12394             tag : 'div',
12395             cls : 'tickable-buttons',
12396             cn : [
12397                 {
12398                     tag : 'button',
12399                     type : 'button',
12400                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12401                     //html : this.triggerText
12402                     html: btn_text_select
12403                 },
12404                 {
12405                     tag : 'button',
12406                     type : 'button',
12407                     name : 'ok',
12408                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12409                     //html : 'Done'
12410                     html: btn_text_done
12411                 },
12412                 {
12413                     tag : 'button',
12414                     type : 'button',
12415                     name : 'cancel',
12416                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12417                     //html : 'Cancel'
12418                     html: btn_text_cancel
12419                 }
12420             ]
12421         };
12422         
12423         if(this.editable){
12424             buttons.cn.unshift({
12425                 tag: 'input',
12426                 cls: 'roo-select2-search-field-input'
12427             });
12428         }
12429         
12430         var _this = this;
12431         
12432         Roo.each(buttons.cn, function(c){
12433             if (_this.size) {
12434                 c.cls += ' btn-' + _this.size;
12435             }
12436
12437             if (_this.disabled) {
12438                 c.disabled = true;
12439             }
12440         });
12441         
12442         var box = {
12443             tag: 'div',
12444             cn: [
12445                 {
12446                     tag: 'input',
12447                     type : 'hidden',
12448                     cls: 'form-hidden-field'
12449                 },
12450                 {
12451                     tag: 'ul',
12452                     cls: 'roo-select2-choices',
12453                     cn:[
12454                         {
12455                             tag: 'li',
12456                             cls: 'roo-select2-search-field',
12457                             cn: [
12458
12459                                 buttons
12460                             ]
12461                         }
12462                     ]
12463                 }
12464             ]
12465         };
12466         
12467         var combobox = {
12468             cls: 'roo-select2-container input-group roo-select2-container-multi',
12469             cn: [
12470                 box
12471 //                {
12472 //                    tag: 'ul',
12473 //                    cls: 'typeahead typeahead-long dropdown-menu',
12474 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12475 //                }
12476             ]
12477         };
12478         
12479         if(this.hasFeedback && !this.allowBlank){
12480             
12481             var feedback = {
12482                 tag: 'span',
12483                 cls: 'glyphicon form-control-feedback'
12484             };
12485
12486             combobox.cn.push(feedback);
12487         }
12488         
12489         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12490             
12491 //                Roo.log("left and has label");
12492             cfg.cn = [
12493                 {
12494                     tag : 'i',
12495                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12496                     tooltip : 'This field is required'
12497                 },
12498                 {
12499                     tag: 'label',
12500                     'for' :  id,
12501                     cls : 'control-label col-sm-' + this.labelWidth,
12502                     html : this.fieldLabel
12503
12504                 },
12505                 {
12506                     cls : "col-sm-" + (12 - this.labelWidth), 
12507                     cn: [
12508                         combobox
12509                     ]
12510                 }
12511
12512             ];
12513
12514             if(this.indicatorpos == 'right'){
12515                 
12516                 cfg.cn = [
12517                     {
12518                         tag: 'label',
12519                         'for' :  id,
12520                         cls : 'control-label col-sm-' + this.labelWidth,
12521                         html : this.fieldLabel
12522
12523                     },
12524                     {
12525                         tag : 'i',
12526                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12527                         tooltip : 'This field is required'
12528                     },
12529                     {
12530                         cls : "col-sm-" + (12 - this.labelWidth), 
12531                         cn: [
12532                             combobox
12533                         ]
12534                     }
12535
12536                 ];
12537             
12538             }
12539                 
12540                 
12541         } else if ( this.fieldLabel.length) {
12542 //                Roo.log(" label");
12543                  cfg.cn = [
12544                     {
12545                         tag : 'i',
12546                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12547                         tooltip : 'This field is required'
12548                     },
12549                     {
12550                         tag: 'label',
12551                         //cls : 'input-group-addon',
12552                         html : this.fieldLabel
12553                         
12554                     },
12555                     
12556                     combobox
12557                     
12558                 ];
12559                 
12560                 if(this.indicatorpos == 'right'){
12561                     
12562                     cfg.cn = [
12563                         {
12564                             tag: 'label',
12565                             //cls : 'input-group-addon',
12566                             html : this.fieldLabel
12567
12568                         },
12569                         
12570                         {
12571                             tag : 'i',
12572                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12573                             tooltip : 'This field is required'
12574                         },
12575                         
12576                         combobox
12577
12578                     ];
12579                 
12580                 }
12581
12582         } else {
12583             
12584 //                Roo.log(" no label && no align");
12585                 cfg = combobox
12586                      
12587                 
12588         }
12589          
12590         var settings=this;
12591         ['xs','sm','md','lg'].map(function(size){
12592             if (settings[size]) {
12593                 cfg.cls += ' col-' + size + '-' + settings[size];
12594             }
12595         });
12596         
12597         return cfg;
12598         
12599     },
12600     
12601     _initEventsCalled : false,
12602     
12603     // private
12604     initEvents: function()
12605     {   
12606         if (this._initEventsCalled) { // as we call render... prevent looping...
12607             return;
12608         }
12609         this._initEventsCalled = true;
12610         
12611         if (!this.store) {
12612             throw "can not find store for combo";
12613         }
12614         
12615         this.store = Roo.factory(this.store, Roo.data);
12616         
12617         // if we are building from html. then this element is so complex, that we can not really
12618         // use the rendered HTML.
12619         // so we have to trash and replace the previous code.
12620         if (Roo.XComponent.build_from_html) {
12621             
12622             // remove this element....
12623             var e = this.el.dom, k=0;
12624             while (e ) { e = e.previousSibling;  ++k;}
12625
12626             this.el.remove();
12627             
12628             this.el=false;
12629             this.rendered = false;
12630             
12631             this.render(this.parent().getChildContainer(true), k);
12632             
12633             
12634             
12635         }
12636         
12637         if(Roo.isIOS && this.useNativeIOS){
12638             this.initIOSView();
12639             return;
12640         }
12641         
12642         /*
12643          * Touch Devices
12644          */
12645         
12646         if(Roo.isTouch && this.mobileTouchView){
12647             this.initTouchView();
12648             return;
12649         }
12650         
12651         if(this.tickable){
12652             this.initTickableEvents();
12653             return;
12654         }
12655         
12656         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12657         
12658         if(this.hiddenName){
12659             
12660             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12661             
12662             this.hiddenField.dom.value =
12663                 this.hiddenValue !== undefined ? this.hiddenValue :
12664                 this.value !== undefined ? this.value : '';
12665
12666             // prevent input submission
12667             this.el.dom.removeAttribute('name');
12668             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12669              
12670              
12671         }
12672         //if(Roo.isGecko){
12673         //    this.el.dom.setAttribute('autocomplete', 'off');
12674         //}
12675         
12676         var cls = 'x-combo-list';
12677         
12678         //this.list = new Roo.Layer({
12679         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12680         //});
12681         
12682         var _this = this;
12683         
12684         (function(){
12685             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12686             _this.list.setWidth(lw);
12687         }).defer(100);
12688         
12689         this.list.on('mouseover', this.onViewOver, this);
12690         this.list.on('mousemove', this.onViewMove, this);
12691         
12692         this.list.on('scroll', this.onViewScroll, this);
12693         
12694         /*
12695         this.list.swallowEvent('mousewheel');
12696         this.assetHeight = 0;
12697
12698         if(this.title){
12699             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12700             this.assetHeight += this.header.getHeight();
12701         }
12702
12703         this.innerList = this.list.createChild({cls:cls+'-inner'});
12704         this.innerList.on('mouseover', this.onViewOver, this);
12705         this.innerList.on('mousemove', this.onViewMove, this);
12706         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12707         
12708         if(this.allowBlank && !this.pageSize && !this.disableClear){
12709             this.footer = this.list.createChild({cls:cls+'-ft'});
12710             this.pageTb = new Roo.Toolbar(this.footer);
12711            
12712         }
12713         if(this.pageSize){
12714             this.footer = this.list.createChild({cls:cls+'-ft'});
12715             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12716                     {pageSize: this.pageSize});
12717             
12718         }
12719         
12720         if (this.pageTb && this.allowBlank && !this.disableClear) {
12721             var _this = this;
12722             this.pageTb.add(new Roo.Toolbar.Fill(), {
12723                 cls: 'x-btn-icon x-btn-clear',
12724                 text: '&#160;',
12725                 handler: function()
12726                 {
12727                     _this.collapse();
12728                     _this.clearValue();
12729                     _this.onSelect(false, -1);
12730                 }
12731             });
12732         }
12733         if (this.footer) {
12734             this.assetHeight += this.footer.getHeight();
12735         }
12736         */
12737             
12738         if(!this.tpl){
12739             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12740         }
12741
12742         this.view = new Roo.View(this.list, this.tpl, {
12743             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12744         });
12745         //this.view.wrapEl.setDisplayed(false);
12746         this.view.on('click', this.onViewClick, this);
12747         
12748         
12749         
12750         this.store.on('beforeload', this.onBeforeLoad, this);
12751         this.store.on('load', this.onLoad, this);
12752         this.store.on('loadexception', this.onLoadException, this);
12753         /*
12754         if(this.resizable){
12755             this.resizer = new Roo.Resizable(this.list,  {
12756                pinned:true, handles:'se'
12757             });
12758             this.resizer.on('resize', function(r, w, h){
12759                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12760                 this.listWidth = w;
12761                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12762                 this.restrictHeight();
12763             }, this);
12764             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12765         }
12766         */
12767         if(!this.editable){
12768             this.editable = true;
12769             this.setEditable(false);
12770         }
12771         
12772         /*
12773         
12774         if (typeof(this.events.add.listeners) != 'undefined') {
12775             
12776             this.addicon = this.wrap.createChild(
12777                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12778        
12779             this.addicon.on('click', function(e) {
12780                 this.fireEvent('add', this);
12781             }, this);
12782         }
12783         if (typeof(this.events.edit.listeners) != 'undefined') {
12784             
12785             this.editicon = this.wrap.createChild(
12786                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12787             if (this.addicon) {
12788                 this.editicon.setStyle('margin-left', '40px');
12789             }
12790             this.editicon.on('click', function(e) {
12791                 
12792                 // we fire even  if inothing is selected..
12793                 this.fireEvent('edit', this, this.lastData );
12794                 
12795             }, this);
12796         }
12797         */
12798         
12799         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12800             "up" : function(e){
12801                 this.inKeyMode = true;
12802                 this.selectPrev();
12803             },
12804
12805             "down" : function(e){
12806                 if(!this.isExpanded()){
12807                     this.onTriggerClick();
12808                 }else{
12809                     this.inKeyMode = true;
12810                     this.selectNext();
12811                 }
12812             },
12813
12814             "enter" : function(e){
12815 //                this.onViewClick();
12816                 //return true;
12817                 this.collapse();
12818                 
12819                 if(this.fireEvent("specialkey", this, e)){
12820                     this.onViewClick(false);
12821                 }
12822                 
12823                 return true;
12824             },
12825
12826             "esc" : function(e){
12827                 this.collapse();
12828             },
12829
12830             "tab" : function(e){
12831                 this.collapse();
12832                 
12833                 if(this.fireEvent("specialkey", this, e)){
12834                     this.onViewClick(false);
12835                 }
12836                 
12837                 return true;
12838             },
12839
12840             scope : this,
12841
12842             doRelay : function(foo, bar, hname){
12843                 if(hname == 'down' || this.scope.isExpanded()){
12844                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12845                 }
12846                 return true;
12847             },
12848
12849             forceKeyDown: true
12850         });
12851         
12852         
12853         this.queryDelay = Math.max(this.queryDelay || 10,
12854                 this.mode == 'local' ? 10 : 250);
12855         
12856         
12857         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12858         
12859         if(this.typeAhead){
12860             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12861         }
12862         if(this.editable !== false){
12863             this.inputEl().on("keyup", this.onKeyUp, this);
12864         }
12865         if(this.forceSelection){
12866             this.inputEl().on('blur', this.doForce, this);
12867         }
12868         
12869         if(this.multiple){
12870             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12871             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12872         }
12873     },
12874     
12875     initTickableEvents: function()
12876     {   
12877         this.createList();
12878         
12879         if(this.hiddenName){
12880             
12881             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12882             
12883             this.hiddenField.dom.value =
12884                 this.hiddenValue !== undefined ? this.hiddenValue :
12885                 this.value !== undefined ? this.value : '';
12886
12887             // prevent input submission
12888             this.el.dom.removeAttribute('name');
12889             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12890              
12891              
12892         }
12893         
12894 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12895         
12896         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12897         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12898         if(this.triggerList){
12899             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12900         }
12901          
12902         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12903         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12904         
12905         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12906         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12907         
12908         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12909         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12910         
12911         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12912         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12913         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12914         
12915         this.okBtn.hide();
12916         this.cancelBtn.hide();
12917         
12918         var _this = this;
12919         
12920         (function(){
12921             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12922             _this.list.setWidth(lw);
12923         }).defer(100);
12924         
12925         this.list.on('mouseover', this.onViewOver, this);
12926         this.list.on('mousemove', this.onViewMove, this);
12927         
12928         this.list.on('scroll', this.onViewScroll, this);
12929         
12930         if(!this.tpl){
12931             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>';
12932         }
12933
12934         this.view = new Roo.View(this.list, this.tpl, {
12935             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12936         });
12937         
12938         //this.view.wrapEl.setDisplayed(false);
12939         this.view.on('click', this.onViewClick, this);
12940         
12941         
12942         
12943         this.store.on('beforeload', this.onBeforeLoad, this);
12944         this.store.on('load', this.onLoad, this);
12945         this.store.on('loadexception', this.onLoadException, this);
12946         
12947         if(this.editable){
12948             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12949                 "up" : function(e){
12950                     this.inKeyMode = true;
12951                     this.selectPrev();
12952                 },
12953
12954                 "down" : function(e){
12955                     this.inKeyMode = true;
12956                     this.selectNext();
12957                 },
12958
12959                 "enter" : function(e){
12960                     if(this.fireEvent("specialkey", this, e)){
12961                         this.onViewClick(false);
12962                     }
12963                     
12964                     return true;
12965                 },
12966
12967                 "esc" : function(e){
12968                     this.onTickableFooterButtonClick(e, false, false);
12969                 },
12970
12971                 "tab" : function(e){
12972                     this.fireEvent("specialkey", this, e);
12973                     
12974                     this.onTickableFooterButtonClick(e, false, false);
12975                     
12976                     return true;
12977                 },
12978
12979                 scope : this,
12980
12981                 doRelay : function(e, fn, key){
12982                     if(this.scope.isExpanded()){
12983                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12984                     }
12985                     return true;
12986                 },
12987
12988                 forceKeyDown: true
12989             });
12990         }
12991         
12992         this.queryDelay = Math.max(this.queryDelay || 10,
12993                 this.mode == 'local' ? 10 : 250);
12994         
12995         
12996         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12997         
12998         if(this.typeAhead){
12999             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13000         }
13001         
13002         if(this.editable !== false){
13003             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13004         }
13005         
13006     },
13007
13008     onDestroy : function(){
13009         if(this.view){
13010             this.view.setStore(null);
13011             this.view.el.removeAllListeners();
13012             this.view.el.remove();
13013             this.view.purgeListeners();
13014         }
13015         if(this.list){
13016             this.list.dom.innerHTML  = '';
13017         }
13018         
13019         if(this.store){
13020             this.store.un('beforeload', this.onBeforeLoad, this);
13021             this.store.un('load', this.onLoad, this);
13022             this.store.un('loadexception', this.onLoadException, this);
13023         }
13024         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13025     },
13026
13027     // private
13028     fireKey : function(e){
13029         if(e.isNavKeyPress() && !this.list.isVisible()){
13030             this.fireEvent("specialkey", this, e);
13031         }
13032     },
13033
13034     // private
13035     onResize: function(w, h){
13036 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13037 //        
13038 //        if(typeof w != 'number'){
13039 //            // we do not handle it!?!?
13040 //            return;
13041 //        }
13042 //        var tw = this.trigger.getWidth();
13043 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13044 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13045 //        var x = w - tw;
13046 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13047 //            
13048 //        //this.trigger.setStyle('left', x+'px');
13049 //        
13050 //        if(this.list && this.listWidth === undefined){
13051 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13052 //            this.list.setWidth(lw);
13053 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13054 //        }
13055         
13056     
13057         
13058     },
13059
13060     /**
13061      * Allow or prevent the user from directly editing the field text.  If false is passed,
13062      * the user will only be able to select from the items defined in the dropdown list.  This method
13063      * is the runtime equivalent of setting the 'editable' config option at config time.
13064      * @param {Boolean} value True to allow the user to directly edit the field text
13065      */
13066     setEditable : function(value){
13067         if(value == this.editable){
13068             return;
13069         }
13070         this.editable = value;
13071         if(!value){
13072             this.inputEl().dom.setAttribute('readOnly', true);
13073             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13074             this.inputEl().addClass('x-combo-noedit');
13075         }else{
13076             this.inputEl().dom.setAttribute('readOnly', false);
13077             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13078             this.inputEl().removeClass('x-combo-noedit');
13079         }
13080     },
13081
13082     // private
13083     
13084     onBeforeLoad : function(combo,opts){
13085         if(!this.hasFocus){
13086             return;
13087         }
13088          if (!opts.add) {
13089             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13090          }
13091         this.restrictHeight();
13092         this.selectedIndex = -1;
13093     },
13094
13095     // private
13096     onLoad : function(){
13097         
13098         this.hasQuery = false;
13099         
13100         if(!this.hasFocus){
13101             return;
13102         }
13103         
13104         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13105             this.loading.hide();
13106         }
13107              
13108         if(this.store.getCount() > 0){
13109             this.expand();
13110             this.restrictHeight();
13111             if(this.lastQuery == this.allQuery){
13112                 if(this.editable && !this.tickable){
13113                     this.inputEl().dom.select();
13114                 }
13115                 
13116                 if(
13117                     !this.selectByValue(this.value, true) &&
13118                     this.autoFocus && 
13119                     (
13120                         !this.store.lastOptions ||
13121                         typeof(this.store.lastOptions.add) == 'undefined' || 
13122                         this.store.lastOptions.add != true
13123                     )
13124                 ){
13125                     this.select(0, true);
13126                 }
13127             }else{
13128                 if(this.autoFocus){
13129                     this.selectNext();
13130                 }
13131                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13132                     this.taTask.delay(this.typeAheadDelay);
13133                 }
13134             }
13135         }else{
13136             this.onEmptyResults();
13137         }
13138         
13139         //this.el.focus();
13140     },
13141     // private
13142     onLoadException : function()
13143     {
13144         this.hasQuery = false;
13145         
13146         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13147             this.loading.hide();
13148         }
13149         
13150         if(this.tickable && this.editable){
13151             return;
13152         }
13153         
13154         this.collapse();
13155         // only causes errors at present
13156         //Roo.log(this.store.reader.jsonData);
13157         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13158             // fixme
13159             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13160         //}
13161         
13162         
13163     },
13164     // private
13165     onTypeAhead : function(){
13166         if(this.store.getCount() > 0){
13167             var r = this.store.getAt(0);
13168             var newValue = r.data[this.displayField];
13169             var len = newValue.length;
13170             var selStart = this.getRawValue().length;
13171             
13172             if(selStart != len){
13173                 this.setRawValue(newValue);
13174                 this.selectText(selStart, newValue.length);
13175             }
13176         }
13177     },
13178
13179     // private
13180     onSelect : function(record, index){
13181         
13182         if(this.fireEvent('beforeselect', this, record, index) !== false){
13183         
13184             this.setFromData(index > -1 ? record.data : false);
13185             
13186             this.collapse();
13187             this.fireEvent('select', this, record, index);
13188         }
13189     },
13190
13191     /**
13192      * Returns the currently selected field value or empty string if no value is set.
13193      * @return {String} value The selected value
13194      */
13195     getValue : function()
13196     {
13197         if(Roo.isIOS && this.useNativeIOS){
13198             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13199         }
13200         
13201         if(this.multiple){
13202             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13203         }
13204         
13205         if(this.valueField){
13206             return typeof this.value != 'undefined' ? this.value : '';
13207         }else{
13208             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13209         }
13210     },
13211     
13212     getRawValue : function()
13213     {
13214         if(Roo.isIOS && this.useNativeIOS){
13215             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13216         }
13217         
13218         var v = this.inputEl().getValue();
13219         
13220         return v;
13221     },
13222
13223     /**
13224      * Clears any text/value currently set in the field
13225      */
13226     clearValue : function(){
13227         
13228         if(this.hiddenField){
13229             this.hiddenField.dom.value = '';
13230         }
13231         this.value = '';
13232         this.setRawValue('');
13233         this.lastSelectionText = '';
13234         this.lastData = false;
13235         
13236         var close = this.closeTriggerEl();
13237         
13238         if(close){
13239             close.hide();
13240         }
13241         
13242         this.validate();
13243         
13244     },
13245
13246     /**
13247      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13248      * will be displayed in the field.  If the value does not match the data value of an existing item,
13249      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13250      * Otherwise the field will be blank (although the value will still be set).
13251      * @param {String} value The value to match
13252      */
13253     setValue : function(v)
13254     {
13255         if(Roo.isIOS && this.useNativeIOS){
13256             this.setIOSValue(v);
13257             return;
13258         }
13259         
13260         if(this.multiple){
13261             this.syncValue();
13262             return;
13263         }
13264         
13265         var text = v;
13266         if(this.valueField){
13267             var r = this.findRecord(this.valueField, v);
13268             if(r){
13269                 text = r.data[this.displayField];
13270             }else if(this.valueNotFoundText !== undefined){
13271                 text = this.valueNotFoundText;
13272             }
13273         }
13274         this.lastSelectionText = text;
13275         if(this.hiddenField){
13276             this.hiddenField.dom.value = v;
13277         }
13278         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13279         this.value = v;
13280         
13281         var close = this.closeTriggerEl();
13282         
13283         if(close){
13284             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13285         }
13286         
13287         this.validate();
13288     },
13289     /**
13290      * @property {Object} the last set data for the element
13291      */
13292     
13293     lastData : false,
13294     /**
13295      * Sets the value of the field based on a object which is related to the record format for the store.
13296      * @param {Object} value the value to set as. or false on reset?
13297      */
13298     setFromData : function(o){
13299         
13300         if(this.multiple){
13301             this.addItem(o);
13302             return;
13303         }
13304             
13305         var dv = ''; // display value
13306         var vv = ''; // value value..
13307         this.lastData = o;
13308         if (this.displayField) {
13309             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13310         } else {
13311             // this is an error condition!!!
13312             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13313         }
13314         
13315         if(this.valueField){
13316             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13317         }
13318         
13319         var close = this.closeTriggerEl();
13320         
13321         if(close){
13322             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13323         }
13324         
13325         if(this.hiddenField){
13326             this.hiddenField.dom.value = vv;
13327             
13328             this.lastSelectionText = dv;
13329             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13330             this.value = vv;
13331             return;
13332         }
13333         // no hidden field.. - we store the value in 'value', but still display
13334         // display field!!!!
13335         this.lastSelectionText = dv;
13336         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13337         this.value = vv;
13338         
13339         
13340         
13341     },
13342     // private
13343     reset : function(){
13344         // overridden so that last data is reset..
13345         
13346         if(this.multiple){
13347             this.clearItem();
13348             return;
13349         }
13350         
13351         this.setValue(this.originalValue);
13352         //this.clearInvalid();
13353         this.lastData = false;
13354         if (this.view) {
13355             this.view.clearSelections();
13356         }
13357         
13358         this.validate();
13359     },
13360     // private
13361     findRecord : function(prop, value){
13362         var record;
13363         if(this.store.getCount() > 0){
13364             this.store.each(function(r){
13365                 if(r.data[prop] == value){
13366                     record = r;
13367                     return false;
13368                 }
13369                 return true;
13370             });
13371         }
13372         return record;
13373     },
13374     
13375     getName: function()
13376     {
13377         // returns hidden if it's set..
13378         if (!this.rendered) {return ''};
13379         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13380         
13381     },
13382     // private
13383     onViewMove : function(e, t){
13384         this.inKeyMode = false;
13385     },
13386
13387     // private
13388     onViewOver : function(e, t){
13389         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13390             return;
13391         }
13392         var item = this.view.findItemFromChild(t);
13393         
13394         if(item){
13395             var index = this.view.indexOf(item);
13396             this.select(index, false);
13397         }
13398     },
13399
13400     // private
13401     onViewClick : function(view, doFocus, el, e)
13402     {
13403         var index = this.view.getSelectedIndexes()[0];
13404         
13405         var r = this.store.getAt(index);
13406         
13407         if(this.tickable){
13408             
13409             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13410                 return;
13411             }
13412             
13413             var rm = false;
13414             var _this = this;
13415             
13416             Roo.each(this.tickItems, function(v,k){
13417                 
13418                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13419                     Roo.log(v);
13420                     _this.tickItems.splice(k, 1);
13421                     
13422                     if(typeof(e) == 'undefined' && view == false){
13423                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13424                     }
13425                     
13426                     rm = true;
13427                     return;
13428                 }
13429             });
13430             
13431             if(rm){
13432                 return;
13433             }
13434             
13435             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13436                 this.tickItems.push(r.data);
13437             }
13438             
13439             if(typeof(e) == 'undefined' && view == false){
13440                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13441             }
13442                     
13443             return;
13444         }
13445         
13446         if(r){
13447             this.onSelect(r, index);
13448         }
13449         if(doFocus !== false && !this.blockFocus){
13450             this.inputEl().focus();
13451         }
13452     },
13453
13454     // private
13455     restrictHeight : function(){
13456         //this.innerList.dom.style.height = '';
13457         //var inner = this.innerList.dom;
13458         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13459         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13460         //this.list.beginUpdate();
13461         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13462         this.list.alignTo(this.inputEl(), this.listAlign);
13463         this.list.alignTo(this.inputEl(), this.listAlign);
13464         //this.list.endUpdate();
13465     },
13466
13467     // private
13468     onEmptyResults : function(){
13469         
13470         if(this.tickable && this.editable){
13471             this.restrictHeight();
13472             return;
13473         }
13474         
13475         this.collapse();
13476     },
13477
13478     /**
13479      * Returns true if the dropdown list is expanded, else false.
13480      */
13481     isExpanded : function(){
13482         return this.list.isVisible();
13483     },
13484
13485     /**
13486      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13487      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13488      * @param {String} value The data value of the item to select
13489      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13490      * selected item if it is not currently in view (defaults to true)
13491      * @return {Boolean} True if the value matched an item in the list, else false
13492      */
13493     selectByValue : function(v, scrollIntoView){
13494         if(v !== undefined && v !== null){
13495             var r = this.findRecord(this.valueField || this.displayField, v);
13496             if(r){
13497                 this.select(this.store.indexOf(r), scrollIntoView);
13498                 return true;
13499             }
13500         }
13501         return false;
13502     },
13503
13504     /**
13505      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13506      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13507      * @param {Number} index The zero-based index of the list item to select
13508      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13509      * selected item if it is not currently in view (defaults to true)
13510      */
13511     select : function(index, scrollIntoView){
13512         this.selectedIndex = index;
13513         this.view.select(index);
13514         if(scrollIntoView !== false){
13515             var el = this.view.getNode(index);
13516             /*
13517              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13518              */
13519             if(el){
13520                 this.list.scrollChildIntoView(el, false);
13521             }
13522         }
13523     },
13524
13525     // private
13526     selectNext : function(){
13527         var ct = this.store.getCount();
13528         if(ct > 0){
13529             if(this.selectedIndex == -1){
13530                 this.select(0);
13531             }else if(this.selectedIndex < ct-1){
13532                 this.select(this.selectedIndex+1);
13533             }
13534         }
13535     },
13536
13537     // private
13538     selectPrev : function(){
13539         var ct = this.store.getCount();
13540         if(ct > 0){
13541             if(this.selectedIndex == -1){
13542                 this.select(0);
13543             }else if(this.selectedIndex != 0){
13544                 this.select(this.selectedIndex-1);
13545             }
13546         }
13547     },
13548
13549     // private
13550     onKeyUp : function(e){
13551         if(this.editable !== false && !e.isSpecialKey()){
13552             this.lastKey = e.getKey();
13553             this.dqTask.delay(this.queryDelay);
13554         }
13555     },
13556
13557     // private
13558     validateBlur : function(){
13559         return !this.list || !this.list.isVisible();   
13560     },
13561
13562     // private
13563     initQuery : function(){
13564         
13565         var v = this.getRawValue();
13566         
13567         if(this.tickable && this.editable){
13568             v = this.tickableInputEl().getValue();
13569         }
13570         
13571         this.doQuery(v);
13572     },
13573
13574     // private
13575     doForce : function(){
13576         if(this.inputEl().dom.value.length > 0){
13577             this.inputEl().dom.value =
13578                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13579              
13580         }
13581     },
13582
13583     /**
13584      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13585      * query allowing the query action to be canceled if needed.
13586      * @param {String} query The SQL query to execute
13587      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13588      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13589      * saved in the current store (defaults to false)
13590      */
13591     doQuery : function(q, forceAll){
13592         
13593         if(q === undefined || q === null){
13594             q = '';
13595         }
13596         var qe = {
13597             query: q,
13598             forceAll: forceAll,
13599             combo: this,
13600             cancel:false
13601         };
13602         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13603             return false;
13604         }
13605         q = qe.query;
13606         
13607         forceAll = qe.forceAll;
13608         if(forceAll === true || (q.length >= this.minChars)){
13609             
13610             this.hasQuery = true;
13611             
13612             if(this.lastQuery != q || this.alwaysQuery){
13613                 this.lastQuery = q;
13614                 if(this.mode == 'local'){
13615                     this.selectedIndex = -1;
13616                     if(forceAll){
13617                         this.store.clearFilter();
13618                     }else{
13619                         
13620                         if(this.specialFilter){
13621                             this.fireEvent('specialfilter', this);
13622                             this.onLoad();
13623                             return;
13624                         }
13625                         
13626                         this.store.filter(this.displayField, q);
13627                     }
13628                     
13629                     this.store.fireEvent("datachanged", this.store);
13630                     
13631                     this.onLoad();
13632                     
13633                     
13634                 }else{
13635                     
13636                     this.store.baseParams[this.queryParam] = q;
13637                     
13638                     var options = {params : this.getParams(q)};
13639                     
13640                     if(this.loadNext){
13641                         options.add = true;
13642                         options.params.start = this.page * this.pageSize;
13643                     }
13644                     
13645                     this.store.load(options);
13646                     
13647                     /*
13648                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13649                      *  we should expand the list on onLoad
13650                      *  so command out it
13651                      */
13652 //                    this.expand();
13653                 }
13654             }else{
13655                 this.selectedIndex = -1;
13656                 this.onLoad();   
13657             }
13658         }
13659         
13660         this.loadNext = false;
13661     },
13662     
13663     // private
13664     getParams : function(q){
13665         var p = {};
13666         //p[this.queryParam] = q;
13667         
13668         if(this.pageSize){
13669             p.start = 0;
13670             p.limit = this.pageSize;
13671         }
13672         return p;
13673     },
13674
13675     /**
13676      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13677      */
13678     collapse : function(){
13679         if(!this.isExpanded()){
13680             return;
13681         }
13682         
13683         this.list.hide();
13684         
13685         this.hasFocus = false;
13686         
13687         if(this.tickable){
13688             this.okBtn.hide();
13689             this.cancelBtn.hide();
13690             this.trigger.show();
13691             
13692             if(this.editable){
13693                 this.tickableInputEl().dom.value = '';
13694                 this.tickableInputEl().blur();
13695             }
13696             
13697         }
13698         
13699         Roo.get(document).un('mousedown', this.collapseIf, this);
13700         Roo.get(document).un('mousewheel', this.collapseIf, this);
13701         if (!this.editable) {
13702             Roo.get(document).un('keydown', this.listKeyPress, this);
13703         }
13704         this.fireEvent('collapse', this);
13705         
13706         this.validate();
13707     },
13708
13709     // private
13710     collapseIf : function(e){
13711         var in_combo  = e.within(this.el);
13712         var in_list =  e.within(this.list);
13713         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13714         
13715         if (in_combo || in_list || is_list) {
13716             //e.stopPropagation();
13717             return;
13718         }
13719         
13720         if(this.tickable){
13721             this.onTickableFooterButtonClick(e, false, false);
13722         }
13723
13724         this.collapse();
13725         
13726     },
13727
13728     /**
13729      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13730      */
13731     expand : function(){
13732        
13733         if(this.isExpanded() || !this.hasFocus){
13734             return;
13735         }
13736         
13737         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13738         this.list.setWidth(lw);
13739         
13740         Roo.log('expand');
13741         
13742         this.list.show();
13743         
13744         this.restrictHeight();
13745         
13746         if(this.tickable){
13747             
13748             this.tickItems = Roo.apply([], this.item);
13749             
13750             this.okBtn.show();
13751             this.cancelBtn.show();
13752             this.trigger.hide();
13753             
13754             if(this.editable){
13755                 this.tickableInputEl().focus();
13756             }
13757             
13758         }
13759         
13760         Roo.get(document).on('mousedown', this.collapseIf, this);
13761         Roo.get(document).on('mousewheel', this.collapseIf, this);
13762         if (!this.editable) {
13763             Roo.get(document).on('keydown', this.listKeyPress, this);
13764         }
13765         
13766         this.fireEvent('expand', this);
13767     },
13768
13769     // private
13770     // Implements the default empty TriggerField.onTriggerClick function
13771     onTriggerClick : function(e)
13772     {
13773         Roo.log('trigger click');
13774         
13775         if(this.disabled || !this.triggerList){
13776             return;
13777         }
13778         
13779         this.page = 0;
13780         this.loadNext = false;
13781         
13782         if(this.isExpanded()){
13783             this.collapse();
13784             if (!this.blockFocus) {
13785                 this.inputEl().focus();
13786             }
13787             
13788         }else {
13789             this.hasFocus = true;
13790             if(this.triggerAction == 'all') {
13791                 this.doQuery(this.allQuery, true);
13792             } else {
13793                 this.doQuery(this.getRawValue());
13794             }
13795             if (!this.blockFocus) {
13796                 this.inputEl().focus();
13797             }
13798         }
13799     },
13800     
13801     onTickableTriggerClick : function(e)
13802     {
13803         if(this.disabled){
13804             return;
13805         }
13806         
13807         this.page = 0;
13808         this.loadNext = false;
13809         this.hasFocus = true;
13810         
13811         if(this.triggerAction == 'all') {
13812             this.doQuery(this.allQuery, true);
13813         } else {
13814             this.doQuery(this.getRawValue());
13815         }
13816     },
13817     
13818     onSearchFieldClick : function(e)
13819     {
13820         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13821             this.onTickableFooterButtonClick(e, false, false);
13822             return;
13823         }
13824         
13825         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13826             return;
13827         }
13828         
13829         this.page = 0;
13830         this.loadNext = false;
13831         this.hasFocus = true;
13832         
13833         if(this.triggerAction == 'all') {
13834             this.doQuery(this.allQuery, true);
13835         } else {
13836             this.doQuery(this.getRawValue());
13837         }
13838     },
13839     
13840     listKeyPress : function(e)
13841     {
13842         //Roo.log('listkeypress');
13843         // scroll to first matching element based on key pres..
13844         if (e.isSpecialKey()) {
13845             return false;
13846         }
13847         var k = String.fromCharCode(e.getKey()).toUpperCase();
13848         //Roo.log(k);
13849         var match  = false;
13850         var csel = this.view.getSelectedNodes();
13851         var cselitem = false;
13852         if (csel.length) {
13853             var ix = this.view.indexOf(csel[0]);
13854             cselitem  = this.store.getAt(ix);
13855             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13856                 cselitem = false;
13857             }
13858             
13859         }
13860         
13861         this.store.each(function(v) { 
13862             if (cselitem) {
13863                 // start at existing selection.
13864                 if (cselitem.id == v.id) {
13865                     cselitem = false;
13866                 }
13867                 return true;
13868             }
13869                 
13870             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13871                 match = this.store.indexOf(v);
13872                 return false;
13873             }
13874             return true;
13875         }, this);
13876         
13877         if (match === false) {
13878             return true; // no more action?
13879         }
13880         // scroll to?
13881         this.view.select(match);
13882         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13883         sn.scrollIntoView(sn.dom.parentNode, false);
13884     },
13885     
13886     onViewScroll : function(e, t){
13887         
13888         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){
13889             return;
13890         }
13891         
13892         this.hasQuery = true;
13893         
13894         this.loading = this.list.select('.loading', true).first();
13895         
13896         if(this.loading === null){
13897             this.list.createChild({
13898                 tag: 'div',
13899                 cls: 'loading roo-select2-more-results roo-select2-active',
13900                 html: 'Loading more results...'
13901             });
13902             
13903             this.loading = this.list.select('.loading', true).first();
13904             
13905             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13906             
13907             this.loading.hide();
13908         }
13909         
13910         this.loading.show();
13911         
13912         var _combo = this;
13913         
13914         this.page++;
13915         this.loadNext = true;
13916         
13917         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13918         
13919         return;
13920     },
13921     
13922     addItem : function(o)
13923     {   
13924         var dv = ''; // display value
13925         
13926         if (this.displayField) {
13927             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13928         } else {
13929             // this is an error condition!!!
13930             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13931         }
13932         
13933         if(!dv.length){
13934             return;
13935         }
13936         
13937         var choice = this.choices.createChild({
13938             tag: 'li',
13939             cls: 'roo-select2-search-choice',
13940             cn: [
13941                 {
13942                     tag: 'div',
13943                     html: dv
13944                 },
13945                 {
13946                     tag: 'a',
13947                     href: '#',
13948                     cls: 'roo-select2-search-choice-close',
13949                     tabindex: '-1'
13950                 }
13951             ]
13952             
13953         }, this.searchField);
13954         
13955         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13956         
13957         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13958         
13959         this.item.push(o);
13960         
13961         this.lastData = o;
13962         
13963         this.syncValue();
13964         
13965         this.inputEl().dom.value = '';
13966         
13967         this.validate();
13968     },
13969     
13970     onRemoveItem : function(e, _self, o)
13971     {
13972         e.preventDefault();
13973         
13974         this.lastItem = Roo.apply([], this.item);
13975         
13976         var index = this.item.indexOf(o.data) * 1;
13977         
13978         if( index < 0){
13979             Roo.log('not this item?!');
13980             return;
13981         }
13982         
13983         this.item.splice(index, 1);
13984         o.item.remove();
13985         
13986         this.syncValue();
13987         
13988         this.fireEvent('remove', this, e);
13989         
13990         this.validate();
13991         
13992     },
13993     
13994     syncValue : function()
13995     {
13996         if(!this.item.length){
13997             this.clearValue();
13998             return;
13999         }
14000             
14001         var value = [];
14002         var _this = this;
14003         Roo.each(this.item, function(i){
14004             if(_this.valueField){
14005                 value.push(i[_this.valueField]);
14006                 return;
14007             }
14008
14009             value.push(i);
14010         });
14011
14012         this.value = value.join(',');
14013
14014         if(this.hiddenField){
14015             this.hiddenField.dom.value = this.value;
14016         }
14017         
14018         this.store.fireEvent("datachanged", this.store);
14019         
14020         this.validate();
14021     },
14022     
14023     clearItem : function()
14024     {
14025         if(!this.multiple){
14026             return;
14027         }
14028         
14029         this.item = [];
14030         
14031         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14032            c.remove();
14033         });
14034         
14035         this.syncValue();
14036         
14037         this.validate();
14038         
14039         if(this.tickable && !Roo.isTouch){
14040             this.view.refresh();
14041         }
14042     },
14043     
14044     inputEl: function ()
14045     {
14046         if(Roo.isIOS && this.useNativeIOS){
14047             return this.el.select('select.roo-ios-select', true).first();
14048         }
14049         
14050         if(Roo.isTouch && this.mobileTouchView){
14051             return this.el.select('input.form-control',true).first();
14052         }
14053         
14054         if(this.tickable){
14055             return this.searchField;
14056         }
14057         
14058         return this.el.select('input.form-control',true).first();
14059     },
14060     
14061     onTickableFooterButtonClick : function(e, btn, el)
14062     {
14063         e.preventDefault();
14064         
14065         this.lastItem = Roo.apply([], this.item);
14066         
14067         if(btn && btn.name == 'cancel'){
14068             this.tickItems = Roo.apply([], this.item);
14069             this.collapse();
14070             return;
14071         }
14072         
14073         this.clearItem();
14074         
14075         var _this = this;
14076         
14077         Roo.each(this.tickItems, function(o){
14078             _this.addItem(o);
14079         });
14080         
14081         this.collapse();
14082         
14083     },
14084     
14085     validate : function()
14086     {
14087         var v = this.getRawValue();
14088         
14089         if(this.multiple){
14090             v = this.getValue();
14091         }
14092         
14093         if(this.disabled || this.allowBlank || v.length){
14094             this.markValid();
14095             return true;
14096         }
14097         
14098         this.markInvalid();
14099         return false;
14100     },
14101     
14102     tickableInputEl : function()
14103     {
14104         if(!this.tickable || !this.editable){
14105             return this.inputEl();
14106         }
14107         
14108         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14109     },
14110     
14111     
14112     getAutoCreateTouchView : function()
14113     {
14114         var id = Roo.id();
14115         
14116         var cfg = {
14117             cls: 'form-group' //input-group
14118         };
14119         
14120         var input =  {
14121             tag: 'input',
14122             id : id,
14123             type : this.inputType,
14124             cls : 'form-control x-combo-noedit',
14125             autocomplete: 'new-password',
14126             placeholder : this.placeholder || '',
14127             readonly : true
14128         };
14129         
14130         if (this.name) {
14131             input.name = this.name;
14132         }
14133         
14134         if (this.size) {
14135             input.cls += ' input-' + this.size;
14136         }
14137         
14138         if (this.disabled) {
14139             input.disabled = true;
14140         }
14141         
14142         var inputblock = {
14143             cls : '',
14144             cn : [
14145                 input
14146             ]
14147         };
14148         
14149         if(this.before){
14150             inputblock.cls += ' input-group';
14151             
14152             inputblock.cn.unshift({
14153                 tag :'span',
14154                 cls : 'input-group-addon',
14155                 html : this.before
14156             });
14157         }
14158         
14159         if(this.removable && !this.multiple){
14160             inputblock.cls += ' roo-removable';
14161             
14162             inputblock.cn.push({
14163                 tag: 'button',
14164                 html : 'x',
14165                 cls : 'roo-combo-removable-btn close'
14166             });
14167         }
14168
14169         if(this.hasFeedback && !this.allowBlank){
14170             
14171             inputblock.cls += ' has-feedback';
14172             
14173             inputblock.cn.push({
14174                 tag: 'span',
14175                 cls: 'glyphicon form-control-feedback'
14176             });
14177             
14178         }
14179         
14180         if (this.after) {
14181             
14182             inputblock.cls += (this.before) ? '' : ' input-group';
14183             
14184             inputblock.cn.push({
14185                 tag :'span',
14186                 cls : 'input-group-addon',
14187                 html : this.after
14188             });
14189         }
14190
14191         var box = {
14192             tag: 'div',
14193             cn: [
14194                 {
14195                     tag: 'input',
14196                     type : 'hidden',
14197                     cls: 'form-hidden-field'
14198                 },
14199                 inputblock
14200             ]
14201             
14202         };
14203         
14204         if(this.multiple){
14205             box = {
14206                 tag: 'div',
14207                 cn: [
14208                     {
14209                         tag: 'input',
14210                         type : 'hidden',
14211                         cls: 'form-hidden-field'
14212                     },
14213                     {
14214                         tag: 'ul',
14215                         cls: 'roo-select2-choices',
14216                         cn:[
14217                             {
14218                                 tag: 'li',
14219                                 cls: 'roo-select2-search-field',
14220                                 cn: [
14221
14222                                     inputblock
14223                                 ]
14224                             }
14225                         ]
14226                     }
14227                 ]
14228             }
14229         };
14230         
14231         var combobox = {
14232             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14233             cn: [
14234                 box
14235             ]
14236         };
14237         
14238         if(!this.multiple && this.showToggleBtn){
14239             
14240             var caret = {
14241                         tag: 'span',
14242                         cls: 'caret'
14243             };
14244             
14245             if (this.caret != false) {
14246                 caret = {
14247                      tag: 'i',
14248                      cls: 'fa fa-' + this.caret
14249                 };
14250                 
14251             }
14252             
14253             combobox.cn.push({
14254                 tag :'span',
14255                 cls : 'input-group-addon btn dropdown-toggle',
14256                 cn : [
14257                     caret,
14258                     {
14259                         tag: 'span',
14260                         cls: 'combobox-clear',
14261                         cn  : [
14262                             {
14263                                 tag : 'i',
14264                                 cls: 'icon-remove'
14265                             }
14266                         ]
14267                     }
14268                 ]
14269
14270             })
14271         }
14272         
14273         if(this.multiple){
14274             combobox.cls += ' roo-select2-container-multi';
14275         }
14276         
14277         var align = this.labelAlign || this.parentLabelAlign();
14278         
14279         cfg.cn = combobox;
14280         
14281         if(this.fieldLabel.length && this.labelWidth){
14282             
14283             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14284             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14285             
14286             cfg.cn = [
14287                 {
14288                    tag : 'i',
14289                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14290                    tooltip : 'This field is required'
14291                 },
14292                 {
14293                     tag: 'label',
14294                     cls : 'control-label ' + lw,
14295                     html : this.fieldLabel
14296
14297                 },
14298                 {
14299                     cls : cw, 
14300                     cn: [
14301                         combobox
14302                     ]
14303                 }
14304             ];
14305             
14306             if(this.indicatorpos == 'right'){
14307                 cfg.cn = [
14308                     {
14309                         tag: 'label',
14310                         cls : 'control-label ' + lw,
14311                         html : this.fieldLabel
14312
14313                     },
14314                     {
14315                        tag : 'i',
14316                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14317                        tooltip : 'This field is required'
14318                     },
14319                     {
14320                         cls : cw, 
14321                         cn: [
14322                             combobox
14323                         ]
14324                     }
14325                 ];
14326             }
14327         }
14328         
14329         var settings = this;
14330         
14331         ['xs','sm','md','lg'].map(function(size){
14332             if (settings[size]) {
14333                 cfg.cls += ' col-' + size + '-' + settings[size];
14334             }
14335         });
14336         
14337         return cfg;
14338     },
14339     
14340     initTouchView : function()
14341     {
14342         this.renderTouchView();
14343         
14344         this.touchViewEl.on('scroll', function(){
14345             this.el.dom.scrollTop = 0;
14346         }, this);
14347         
14348         this.originalValue = this.getValue();
14349         
14350         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14351         
14352         this.inputEl().on("click", this.showTouchView, this);
14353         if (this.triggerEl) {
14354             this.triggerEl.on("click", this.showTouchView, this);
14355         }
14356         
14357         
14358         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14359         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14360         
14361         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14362         
14363         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14364         this.store.on('load', this.onTouchViewLoad, this);
14365         this.store.on('loadexception', this.onTouchViewLoadException, this);
14366         
14367         if(this.hiddenName){
14368             
14369             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14370             
14371             this.hiddenField.dom.value =
14372                 this.hiddenValue !== undefined ? this.hiddenValue :
14373                 this.value !== undefined ? this.value : '';
14374         
14375             this.el.dom.removeAttribute('name');
14376             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14377         }
14378         
14379         if(this.multiple){
14380             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14381             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14382         }
14383         
14384         if(this.removable && !this.multiple){
14385             var close = this.closeTriggerEl();
14386             if(close){
14387                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14388                 close.on('click', this.removeBtnClick, this, close);
14389             }
14390         }
14391         /*
14392          * fix the bug in Safari iOS8
14393          */
14394         this.inputEl().on("focus", function(e){
14395             document.activeElement.blur();
14396         }, this);
14397         
14398         return;
14399         
14400         
14401     },
14402     
14403     renderTouchView : function()
14404     {
14405         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14406         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14407         
14408         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14409         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14410         
14411         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14412         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14413         this.touchViewBodyEl.setStyle('overflow', 'auto');
14414         
14415         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14416         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14417         
14418         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14419         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14420         
14421     },
14422     
14423     showTouchView : function()
14424     {
14425         if(this.disabled){
14426             return;
14427         }
14428         
14429         this.touchViewHeaderEl.hide();
14430
14431         if(this.modalTitle.length){
14432             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14433             this.touchViewHeaderEl.show();
14434         }
14435
14436         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14437         this.touchViewEl.show();
14438
14439         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14440         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14441                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14442
14443         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14444
14445         if(this.modalTitle.length){
14446             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14447         }
14448         
14449         this.touchViewBodyEl.setHeight(bodyHeight);
14450
14451         if(this.animate){
14452             var _this = this;
14453             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14454         }else{
14455             this.touchViewEl.addClass('in');
14456         }
14457
14458         this.doTouchViewQuery();
14459         
14460     },
14461     
14462     hideTouchView : function()
14463     {
14464         this.touchViewEl.removeClass('in');
14465
14466         if(this.animate){
14467             var _this = this;
14468             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14469         }else{
14470             this.touchViewEl.setStyle('display', 'none');
14471         }
14472         
14473     },
14474     
14475     setTouchViewValue : function()
14476     {
14477         if(this.multiple){
14478             this.clearItem();
14479         
14480             var _this = this;
14481
14482             Roo.each(this.tickItems, function(o){
14483                 this.addItem(o);
14484             }, this);
14485         }
14486         
14487         this.hideTouchView();
14488     },
14489     
14490     doTouchViewQuery : function()
14491     {
14492         var qe = {
14493             query: '',
14494             forceAll: true,
14495             combo: this,
14496             cancel:false
14497         };
14498         
14499         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14500             return false;
14501         }
14502         
14503         if(!this.alwaysQuery || this.mode == 'local'){
14504             this.onTouchViewLoad();
14505             return;
14506         }
14507         
14508         this.store.load();
14509     },
14510     
14511     onTouchViewBeforeLoad : function(combo,opts)
14512     {
14513         return;
14514     },
14515
14516     // private
14517     onTouchViewLoad : function()
14518     {
14519         if(this.store.getCount() < 1){
14520             this.onTouchViewEmptyResults();
14521             return;
14522         }
14523         
14524         this.clearTouchView();
14525         
14526         var rawValue = this.getRawValue();
14527         
14528         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14529         
14530         this.tickItems = [];
14531         
14532         this.store.data.each(function(d, rowIndex){
14533             var row = this.touchViewListGroup.createChild(template);
14534             
14535             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14536                 row.addClass(d.data.cls);
14537             }
14538             
14539             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14540                 var cfg = {
14541                     data : d.data,
14542                     html : d.data[this.displayField]
14543                 };
14544                 
14545                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14546                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14547                 }
14548             }
14549             row.removeClass('selected');
14550             if(!this.multiple && this.valueField &&
14551                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14552             {
14553                 // radio buttons..
14554                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14555                 row.addClass('selected');
14556             }
14557             
14558             if(this.multiple && this.valueField &&
14559                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14560             {
14561                 
14562                 // checkboxes...
14563                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14564                 this.tickItems.push(d.data);
14565             }
14566             
14567             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14568             
14569         }, this);
14570         
14571         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14572         
14573         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14574
14575         if(this.modalTitle.length){
14576             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14577         }
14578
14579         var listHeight = this.touchViewListGroup.getHeight();
14580         
14581         var _this = this;
14582         
14583         if(firstChecked && listHeight > bodyHeight){
14584             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14585         }
14586         
14587     },
14588     
14589     onTouchViewLoadException : function()
14590     {
14591         this.hideTouchView();
14592     },
14593     
14594     onTouchViewEmptyResults : function()
14595     {
14596         this.clearTouchView();
14597         
14598         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14599         
14600         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14601         
14602     },
14603     
14604     clearTouchView : function()
14605     {
14606         this.touchViewListGroup.dom.innerHTML = '';
14607     },
14608     
14609     onTouchViewClick : function(e, el, o)
14610     {
14611         e.preventDefault();
14612         
14613         var row = o.row;
14614         var rowIndex = o.rowIndex;
14615         
14616         var r = this.store.getAt(rowIndex);
14617         
14618         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14619             
14620             if(!this.multiple){
14621                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14622                     c.dom.removeAttribute('checked');
14623                 }, this);
14624
14625                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14626
14627                 this.setFromData(r.data);
14628
14629                 var close = this.closeTriggerEl();
14630
14631                 if(close){
14632                     close.show();
14633                 }
14634
14635                 this.hideTouchView();
14636
14637                 this.fireEvent('select', this, r, rowIndex);
14638
14639                 return;
14640             }
14641
14642             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14643                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14644                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14645                 return;
14646             }
14647
14648             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14649             this.addItem(r.data);
14650             this.tickItems.push(r.data);
14651         }
14652     },
14653     
14654     getAutoCreateNativeIOS : function()
14655     {
14656         var cfg = {
14657             cls: 'form-group' //input-group,
14658         };
14659         
14660         var combobox =  {
14661             tag: 'select',
14662             cls : 'roo-ios-select'
14663         };
14664         
14665         if (this.name) {
14666             combobox.name = this.name;
14667         }
14668         
14669         if (this.disabled) {
14670             combobox.disabled = true;
14671         }
14672         
14673         var settings = this;
14674         
14675         ['xs','sm','md','lg'].map(function(size){
14676             if (settings[size]) {
14677                 cfg.cls += ' col-' + size + '-' + settings[size];
14678             }
14679         });
14680         
14681         cfg.cn = combobox;
14682         
14683         return cfg;
14684         
14685     },
14686     
14687     initIOSView : function()
14688     {
14689         this.store.on('load', this.onIOSViewLoad, this);
14690         
14691         return;
14692     },
14693     
14694     onIOSViewLoad : function()
14695     {
14696         if(this.store.getCount() < 1){
14697             return;
14698         }
14699         
14700         this.clearIOSView();
14701         
14702         if(this.allowBlank) {
14703             
14704             var default_text = '-- SELECT --';
14705             
14706             var opt = this.inputEl().createChild({
14707                 tag: 'option',
14708                 value : 0,
14709                 html : default_text
14710             });
14711             
14712             var o = {};
14713             o[this.valueField] = 0;
14714             o[this.displayField] = default_text;
14715             
14716             this.ios_options.push({
14717                 data : o,
14718                 el : opt
14719             });
14720             
14721         }
14722         
14723         this.store.data.each(function(d, rowIndex){
14724             
14725             var html = '';
14726             
14727             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14728                 html = d.data[this.displayField];
14729             }
14730             
14731             var value = '';
14732             
14733             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14734                 value = d.data[this.valueField];
14735             }
14736             
14737             var option = {
14738                 tag: 'option',
14739                 value : value,
14740                 html : html
14741             };
14742             
14743             if(this.value == d.data[this.valueField]){
14744                 option['selected'] = true;
14745             }
14746             
14747             var opt = this.inputEl().createChild(option);
14748             
14749             this.ios_options.push({
14750                 data : d.data,
14751                 el : opt
14752             });
14753             
14754         }, this);
14755         
14756         this.inputEl().on('change', function(){
14757            this.fireEvent('select', this);
14758         }, this);
14759         
14760     },
14761     
14762     clearIOSView: function()
14763     {
14764         this.inputEl().dom.innerHTML = '';
14765         
14766         this.ios_options = [];
14767     },
14768     
14769     setIOSValue: function(v)
14770     {
14771         this.value = v;
14772         
14773         if(!this.ios_options){
14774             return;
14775         }
14776         
14777         Roo.each(this.ios_options, function(opts){
14778            
14779            opts.el.dom.removeAttribute('selected');
14780            
14781            if(opts.data[this.valueField] != v){
14782                return;
14783            }
14784            
14785            opts.el.dom.setAttribute('selected', true);
14786            
14787         }, this);
14788     }
14789
14790     /** 
14791     * @cfg {Boolean} grow 
14792     * @hide 
14793     */
14794     /** 
14795     * @cfg {Number} growMin 
14796     * @hide 
14797     */
14798     /** 
14799     * @cfg {Number} growMax 
14800     * @hide 
14801     */
14802     /**
14803      * @hide
14804      * @method autoSize
14805      */
14806 });
14807
14808 Roo.apply(Roo.bootstrap.ComboBox,  {
14809     
14810     header : {
14811         tag: 'div',
14812         cls: 'modal-header',
14813         cn: [
14814             {
14815                 tag: 'h4',
14816                 cls: 'modal-title'
14817             }
14818         ]
14819     },
14820     
14821     body : {
14822         tag: 'div',
14823         cls: 'modal-body',
14824         cn: [
14825             {
14826                 tag: 'ul',
14827                 cls: 'list-group'
14828             }
14829         ]
14830     },
14831     
14832     listItemRadio : {
14833         tag: 'li',
14834         cls: 'list-group-item',
14835         cn: [
14836             {
14837                 tag: 'span',
14838                 cls: 'roo-combobox-list-group-item-value'
14839             },
14840             {
14841                 tag: 'div',
14842                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14843                 cn: [
14844                     {
14845                         tag: 'input',
14846                         type: 'radio'
14847                     },
14848                     {
14849                         tag: 'label'
14850                     }
14851                 ]
14852             }
14853         ]
14854     },
14855     
14856     listItemCheckbox : {
14857         tag: 'li',
14858         cls: 'list-group-item',
14859         cn: [
14860             {
14861                 tag: 'span',
14862                 cls: 'roo-combobox-list-group-item-value'
14863             },
14864             {
14865                 tag: 'div',
14866                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14867                 cn: [
14868                     {
14869                         tag: 'input',
14870                         type: 'checkbox'
14871                     },
14872                     {
14873                         tag: 'label'
14874                     }
14875                 ]
14876             }
14877         ]
14878     },
14879     
14880     emptyResult : {
14881         tag: 'div',
14882         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14883     },
14884     
14885     footer : {
14886         tag: 'div',
14887         cls: 'modal-footer',
14888         cn: [
14889             {
14890                 tag: 'div',
14891                 cls: 'row',
14892                 cn: [
14893                     {
14894                         tag: 'div',
14895                         cls: 'col-xs-6 text-left',
14896                         cn: {
14897                             tag: 'button',
14898                             cls: 'btn btn-danger roo-touch-view-cancel',
14899                             html: 'Cancel'
14900                         }
14901                     },
14902                     {
14903                         tag: 'div',
14904                         cls: 'col-xs-6 text-right',
14905                         cn: {
14906                             tag: 'button',
14907                             cls: 'btn btn-success roo-touch-view-ok',
14908                             html: 'OK'
14909                         }
14910                     }
14911                 ]
14912             }
14913         ]
14914         
14915     }
14916 });
14917
14918 Roo.apply(Roo.bootstrap.ComboBox,  {
14919     
14920     touchViewTemplate : {
14921         tag: 'div',
14922         cls: 'modal fade roo-combobox-touch-view',
14923         cn: [
14924             {
14925                 tag: 'div',
14926                 cls: 'modal-dialog',
14927                 style : 'position:fixed', // we have to fix position....
14928                 cn: [
14929                     {
14930                         tag: 'div',
14931                         cls: 'modal-content',
14932                         cn: [
14933                             Roo.bootstrap.ComboBox.header,
14934                             Roo.bootstrap.ComboBox.body,
14935                             Roo.bootstrap.ComboBox.footer
14936                         ]
14937                     }
14938                 ]
14939             }
14940         ]
14941     }
14942 });/*
14943  * Based on:
14944  * Ext JS Library 1.1.1
14945  * Copyright(c) 2006-2007, Ext JS, LLC.
14946  *
14947  * Originally Released Under LGPL - original licence link has changed is not relivant.
14948  *
14949  * Fork - LGPL
14950  * <script type="text/javascript">
14951  */
14952
14953 /**
14954  * @class Roo.View
14955  * @extends Roo.util.Observable
14956  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14957  * This class also supports single and multi selection modes. <br>
14958  * Create a data model bound view:
14959  <pre><code>
14960  var store = new Roo.data.Store(...);
14961
14962  var view = new Roo.View({
14963     el : "my-element",
14964     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14965  
14966     singleSelect: true,
14967     selectedClass: "ydataview-selected",
14968     store: store
14969  });
14970
14971  // listen for node click?
14972  view.on("click", function(vw, index, node, e){
14973  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14974  });
14975
14976  // load XML data
14977  dataModel.load("foobar.xml");
14978  </code></pre>
14979  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14980  * <br><br>
14981  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14982  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14983  * 
14984  * Note: old style constructor is still suported (container, template, config)
14985  * 
14986  * @constructor
14987  * Create a new View
14988  * @param {Object} config The config object
14989  * 
14990  */
14991 Roo.View = function(config, depreciated_tpl, depreciated_config){
14992     
14993     this.parent = false;
14994     
14995     if (typeof(depreciated_tpl) == 'undefined') {
14996         // new way.. - universal constructor.
14997         Roo.apply(this, config);
14998         this.el  = Roo.get(this.el);
14999     } else {
15000         // old format..
15001         this.el  = Roo.get(config);
15002         this.tpl = depreciated_tpl;
15003         Roo.apply(this, depreciated_config);
15004     }
15005     this.wrapEl  = this.el.wrap().wrap();
15006     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15007     
15008     
15009     if(typeof(this.tpl) == "string"){
15010         this.tpl = new Roo.Template(this.tpl);
15011     } else {
15012         // support xtype ctors..
15013         this.tpl = new Roo.factory(this.tpl, Roo);
15014     }
15015     
15016     
15017     this.tpl.compile();
15018     
15019     /** @private */
15020     this.addEvents({
15021         /**
15022          * @event beforeclick
15023          * Fires before a click is processed. Returns false to cancel the default action.
15024          * @param {Roo.View} this
15025          * @param {Number} index The index of the target node
15026          * @param {HTMLElement} node The target node
15027          * @param {Roo.EventObject} e The raw event object
15028          */
15029             "beforeclick" : true,
15030         /**
15031          * @event click
15032          * Fires when a template node is clicked.
15033          * @param {Roo.View} this
15034          * @param {Number} index The index of the target node
15035          * @param {HTMLElement} node The target node
15036          * @param {Roo.EventObject} e The raw event object
15037          */
15038             "click" : true,
15039         /**
15040          * @event dblclick
15041          * Fires when a template node is double clicked.
15042          * @param {Roo.View} this
15043          * @param {Number} index The index of the target node
15044          * @param {HTMLElement} node The target node
15045          * @param {Roo.EventObject} e The raw event object
15046          */
15047             "dblclick" : true,
15048         /**
15049          * @event contextmenu
15050          * Fires when a template node is right clicked.
15051          * @param {Roo.View} this
15052          * @param {Number} index The index of the target node
15053          * @param {HTMLElement} node The target node
15054          * @param {Roo.EventObject} e The raw event object
15055          */
15056             "contextmenu" : true,
15057         /**
15058          * @event selectionchange
15059          * Fires when the selected nodes change.
15060          * @param {Roo.View} this
15061          * @param {Array} selections Array of the selected nodes
15062          */
15063             "selectionchange" : true,
15064     
15065         /**
15066          * @event beforeselect
15067          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15068          * @param {Roo.View} this
15069          * @param {HTMLElement} node The node to be selected
15070          * @param {Array} selections Array of currently selected nodes
15071          */
15072             "beforeselect" : true,
15073         /**
15074          * @event preparedata
15075          * Fires on every row to render, to allow you to change the data.
15076          * @param {Roo.View} this
15077          * @param {Object} data to be rendered (change this)
15078          */
15079           "preparedata" : true
15080           
15081           
15082         });
15083
15084
15085
15086     this.el.on({
15087         "click": this.onClick,
15088         "dblclick": this.onDblClick,
15089         "contextmenu": this.onContextMenu,
15090         scope:this
15091     });
15092
15093     this.selections = [];
15094     this.nodes = [];
15095     this.cmp = new Roo.CompositeElementLite([]);
15096     if(this.store){
15097         this.store = Roo.factory(this.store, Roo.data);
15098         this.setStore(this.store, true);
15099     }
15100     
15101     if ( this.footer && this.footer.xtype) {
15102            
15103          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15104         
15105         this.footer.dataSource = this.store;
15106         this.footer.container = fctr;
15107         this.footer = Roo.factory(this.footer, Roo);
15108         fctr.insertFirst(this.el);
15109         
15110         // this is a bit insane - as the paging toolbar seems to detach the el..
15111 //        dom.parentNode.parentNode.parentNode
15112          // they get detached?
15113     }
15114     
15115     
15116     Roo.View.superclass.constructor.call(this);
15117     
15118     
15119 };
15120
15121 Roo.extend(Roo.View, Roo.util.Observable, {
15122     
15123      /**
15124      * @cfg {Roo.data.Store} store Data store to load data from.
15125      */
15126     store : false,
15127     
15128     /**
15129      * @cfg {String|Roo.Element} el The container element.
15130      */
15131     el : '',
15132     
15133     /**
15134      * @cfg {String|Roo.Template} tpl The template used by this View 
15135      */
15136     tpl : false,
15137     /**
15138      * @cfg {String} dataName the named area of the template to use as the data area
15139      *                          Works with domtemplates roo-name="name"
15140      */
15141     dataName: false,
15142     /**
15143      * @cfg {String} selectedClass The css class to add to selected nodes
15144      */
15145     selectedClass : "x-view-selected",
15146      /**
15147      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15148      */
15149     emptyText : "",
15150     
15151     /**
15152      * @cfg {String} text to display on mask (default Loading)
15153      */
15154     mask : false,
15155     /**
15156      * @cfg {Boolean} multiSelect Allow multiple selection
15157      */
15158     multiSelect : false,
15159     /**
15160      * @cfg {Boolean} singleSelect Allow single selection
15161      */
15162     singleSelect:  false,
15163     
15164     /**
15165      * @cfg {Boolean} toggleSelect - selecting 
15166      */
15167     toggleSelect : false,
15168     
15169     /**
15170      * @cfg {Boolean} tickable - selecting 
15171      */
15172     tickable : false,
15173     
15174     /**
15175      * Returns the element this view is bound to.
15176      * @return {Roo.Element}
15177      */
15178     getEl : function(){
15179         return this.wrapEl;
15180     },
15181     
15182     
15183
15184     /**
15185      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15186      */
15187     refresh : function(){
15188         //Roo.log('refresh');
15189         var t = this.tpl;
15190         
15191         // if we are using something like 'domtemplate', then
15192         // the what gets used is:
15193         // t.applySubtemplate(NAME, data, wrapping data..)
15194         // the outer template then get' applied with
15195         //     the store 'extra data'
15196         // and the body get's added to the
15197         //      roo-name="data" node?
15198         //      <span class='roo-tpl-{name}'></span> ?????
15199         
15200         
15201         
15202         this.clearSelections();
15203         this.el.update("");
15204         var html = [];
15205         var records = this.store.getRange();
15206         if(records.length < 1) {
15207             
15208             // is this valid??  = should it render a template??
15209             
15210             this.el.update(this.emptyText);
15211             return;
15212         }
15213         var el = this.el;
15214         if (this.dataName) {
15215             this.el.update(t.apply(this.store.meta)); //????
15216             el = this.el.child('.roo-tpl-' + this.dataName);
15217         }
15218         
15219         for(var i = 0, len = records.length; i < len; i++){
15220             var data = this.prepareData(records[i].data, i, records[i]);
15221             this.fireEvent("preparedata", this, data, i, records[i]);
15222             
15223             var d = Roo.apply({}, data);
15224             
15225             if(this.tickable){
15226                 Roo.apply(d, {'roo-id' : Roo.id()});
15227                 
15228                 var _this = this;
15229             
15230                 Roo.each(this.parent.item, function(item){
15231                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15232                         return;
15233                     }
15234                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15235                 });
15236             }
15237             
15238             html[html.length] = Roo.util.Format.trim(
15239                 this.dataName ?
15240                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15241                     t.apply(d)
15242             );
15243         }
15244         
15245         
15246         
15247         el.update(html.join(""));
15248         this.nodes = el.dom.childNodes;
15249         this.updateIndexes(0);
15250     },
15251     
15252
15253     /**
15254      * Function to override to reformat the data that is sent to
15255      * the template for each node.
15256      * DEPRICATED - use the preparedata event handler.
15257      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15258      * a JSON object for an UpdateManager bound view).
15259      */
15260     prepareData : function(data, index, record)
15261     {
15262         this.fireEvent("preparedata", this, data, index, record);
15263         return data;
15264     },
15265
15266     onUpdate : function(ds, record){
15267         // Roo.log('on update');   
15268         this.clearSelections();
15269         var index = this.store.indexOf(record);
15270         var n = this.nodes[index];
15271         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15272         n.parentNode.removeChild(n);
15273         this.updateIndexes(index, index);
15274     },
15275
15276     
15277     
15278 // --------- FIXME     
15279     onAdd : function(ds, records, index)
15280     {
15281         //Roo.log(['on Add', ds, records, index] );        
15282         this.clearSelections();
15283         if(this.nodes.length == 0){
15284             this.refresh();
15285             return;
15286         }
15287         var n = this.nodes[index];
15288         for(var i = 0, len = records.length; i < len; i++){
15289             var d = this.prepareData(records[i].data, i, records[i]);
15290             if(n){
15291                 this.tpl.insertBefore(n, d);
15292             }else{
15293                 
15294                 this.tpl.append(this.el, d);
15295             }
15296         }
15297         this.updateIndexes(index);
15298     },
15299
15300     onRemove : function(ds, record, index){
15301        // Roo.log('onRemove');
15302         this.clearSelections();
15303         var el = this.dataName  ?
15304             this.el.child('.roo-tpl-' + this.dataName) :
15305             this.el; 
15306         
15307         el.dom.removeChild(this.nodes[index]);
15308         this.updateIndexes(index);
15309     },
15310
15311     /**
15312      * Refresh an individual node.
15313      * @param {Number} index
15314      */
15315     refreshNode : function(index){
15316         this.onUpdate(this.store, this.store.getAt(index));
15317     },
15318
15319     updateIndexes : function(startIndex, endIndex){
15320         var ns = this.nodes;
15321         startIndex = startIndex || 0;
15322         endIndex = endIndex || ns.length - 1;
15323         for(var i = startIndex; i <= endIndex; i++){
15324             ns[i].nodeIndex = i;
15325         }
15326     },
15327
15328     /**
15329      * Changes the data store this view uses and refresh the view.
15330      * @param {Store} store
15331      */
15332     setStore : function(store, initial){
15333         if(!initial && this.store){
15334             this.store.un("datachanged", this.refresh);
15335             this.store.un("add", this.onAdd);
15336             this.store.un("remove", this.onRemove);
15337             this.store.un("update", this.onUpdate);
15338             this.store.un("clear", this.refresh);
15339             this.store.un("beforeload", this.onBeforeLoad);
15340             this.store.un("load", this.onLoad);
15341             this.store.un("loadexception", this.onLoad);
15342         }
15343         if(store){
15344           
15345             store.on("datachanged", this.refresh, this);
15346             store.on("add", this.onAdd, this);
15347             store.on("remove", this.onRemove, this);
15348             store.on("update", this.onUpdate, this);
15349             store.on("clear", this.refresh, this);
15350             store.on("beforeload", this.onBeforeLoad, this);
15351             store.on("load", this.onLoad, this);
15352             store.on("loadexception", this.onLoad, this);
15353         }
15354         
15355         if(store){
15356             this.refresh();
15357         }
15358     },
15359     /**
15360      * onbeforeLoad - masks the loading area.
15361      *
15362      */
15363     onBeforeLoad : function(store,opts)
15364     {
15365          //Roo.log('onBeforeLoad');   
15366         if (!opts.add) {
15367             this.el.update("");
15368         }
15369         this.el.mask(this.mask ? this.mask : "Loading" ); 
15370     },
15371     onLoad : function ()
15372     {
15373         this.el.unmask();
15374     },
15375     
15376
15377     /**
15378      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15379      * @param {HTMLElement} node
15380      * @return {HTMLElement} The template node
15381      */
15382     findItemFromChild : function(node){
15383         var el = this.dataName  ?
15384             this.el.child('.roo-tpl-' + this.dataName,true) :
15385             this.el.dom; 
15386         
15387         if(!node || node.parentNode == el){
15388                     return node;
15389             }
15390             var p = node.parentNode;
15391             while(p && p != el){
15392             if(p.parentNode == el){
15393                 return p;
15394             }
15395             p = p.parentNode;
15396         }
15397             return null;
15398     },
15399
15400     /** @ignore */
15401     onClick : function(e){
15402         var item = this.findItemFromChild(e.getTarget());
15403         if(item){
15404             var index = this.indexOf(item);
15405             if(this.onItemClick(item, index, e) !== false){
15406                 this.fireEvent("click", this, index, item, e);
15407             }
15408         }else{
15409             this.clearSelections();
15410         }
15411     },
15412
15413     /** @ignore */
15414     onContextMenu : function(e){
15415         var item = this.findItemFromChild(e.getTarget());
15416         if(item){
15417             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15418         }
15419     },
15420
15421     /** @ignore */
15422     onDblClick : function(e){
15423         var item = this.findItemFromChild(e.getTarget());
15424         if(item){
15425             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15426         }
15427     },
15428
15429     onItemClick : function(item, index, e)
15430     {
15431         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15432             return false;
15433         }
15434         if (this.toggleSelect) {
15435             var m = this.isSelected(item) ? 'unselect' : 'select';
15436             //Roo.log(m);
15437             var _t = this;
15438             _t[m](item, true, false);
15439             return true;
15440         }
15441         if(this.multiSelect || this.singleSelect){
15442             if(this.multiSelect && e.shiftKey && this.lastSelection){
15443                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15444             }else{
15445                 this.select(item, this.multiSelect && e.ctrlKey);
15446                 this.lastSelection = item;
15447             }
15448             
15449             if(!this.tickable){
15450                 e.preventDefault();
15451             }
15452             
15453         }
15454         return true;
15455     },
15456
15457     /**
15458      * Get the number of selected nodes.
15459      * @return {Number}
15460      */
15461     getSelectionCount : function(){
15462         return this.selections.length;
15463     },
15464
15465     /**
15466      * Get the currently selected nodes.
15467      * @return {Array} An array of HTMLElements
15468      */
15469     getSelectedNodes : function(){
15470         return this.selections;
15471     },
15472
15473     /**
15474      * Get the indexes of the selected nodes.
15475      * @return {Array}
15476      */
15477     getSelectedIndexes : function(){
15478         var indexes = [], s = this.selections;
15479         for(var i = 0, len = s.length; i < len; i++){
15480             indexes.push(s[i].nodeIndex);
15481         }
15482         return indexes;
15483     },
15484
15485     /**
15486      * Clear all selections
15487      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15488      */
15489     clearSelections : function(suppressEvent){
15490         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15491             this.cmp.elements = this.selections;
15492             this.cmp.removeClass(this.selectedClass);
15493             this.selections = [];
15494             if(!suppressEvent){
15495                 this.fireEvent("selectionchange", this, this.selections);
15496             }
15497         }
15498     },
15499
15500     /**
15501      * Returns true if the passed node is selected
15502      * @param {HTMLElement/Number} node The node or node index
15503      * @return {Boolean}
15504      */
15505     isSelected : function(node){
15506         var s = this.selections;
15507         if(s.length < 1){
15508             return false;
15509         }
15510         node = this.getNode(node);
15511         return s.indexOf(node) !== -1;
15512     },
15513
15514     /**
15515      * Selects nodes.
15516      * @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
15517      * @param {Boolean} keepExisting (optional) true to keep existing selections
15518      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15519      */
15520     select : function(nodeInfo, keepExisting, suppressEvent){
15521         if(nodeInfo instanceof Array){
15522             if(!keepExisting){
15523                 this.clearSelections(true);
15524             }
15525             for(var i = 0, len = nodeInfo.length; i < len; i++){
15526                 this.select(nodeInfo[i], true, true);
15527             }
15528             return;
15529         } 
15530         var node = this.getNode(nodeInfo);
15531         if(!node || this.isSelected(node)){
15532             return; // already selected.
15533         }
15534         if(!keepExisting){
15535             this.clearSelections(true);
15536         }
15537         
15538         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15539             Roo.fly(node).addClass(this.selectedClass);
15540             this.selections.push(node);
15541             if(!suppressEvent){
15542                 this.fireEvent("selectionchange", this, this.selections);
15543             }
15544         }
15545         
15546         
15547     },
15548       /**
15549      * Unselects nodes.
15550      * @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
15551      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15553      */
15554     unselect : function(nodeInfo, keepExisting, suppressEvent)
15555     {
15556         if(nodeInfo instanceof Array){
15557             Roo.each(this.selections, function(s) {
15558                 this.unselect(s, nodeInfo);
15559             }, this);
15560             return;
15561         }
15562         var node = this.getNode(nodeInfo);
15563         if(!node || !this.isSelected(node)){
15564             //Roo.log("not selected");
15565             return; // not selected.
15566         }
15567         // fireevent???
15568         var ns = [];
15569         Roo.each(this.selections, function(s) {
15570             if (s == node ) {
15571                 Roo.fly(node).removeClass(this.selectedClass);
15572
15573                 return;
15574             }
15575             ns.push(s);
15576         },this);
15577         
15578         this.selections= ns;
15579         this.fireEvent("selectionchange", this, this.selections);
15580     },
15581
15582     /**
15583      * Gets a template node.
15584      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15585      * @return {HTMLElement} The node or null if it wasn't found
15586      */
15587     getNode : function(nodeInfo){
15588         if(typeof nodeInfo == "string"){
15589             return document.getElementById(nodeInfo);
15590         }else if(typeof nodeInfo == "number"){
15591             return this.nodes[nodeInfo];
15592         }
15593         return nodeInfo;
15594     },
15595
15596     /**
15597      * Gets a range template nodes.
15598      * @param {Number} startIndex
15599      * @param {Number} endIndex
15600      * @return {Array} An array of nodes
15601      */
15602     getNodes : function(start, end){
15603         var ns = this.nodes;
15604         start = start || 0;
15605         end = typeof end == "undefined" ? ns.length - 1 : end;
15606         var nodes = [];
15607         if(start <= end){
15608             for(var i = start; i <= end; i++){
15609                 nodes.push(ns[i]);
15610             }
15611         } else{
15612             for(var i = start; i >= end; i--){
15613                 nodes.push(ns[i]);
15614             }
15615         }
15616         return nodes;
15617     },
15618
15619     /**
15620      * Finds the index of the passed node
15621      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15622      * @return {Number} The index of the node or -1
15623      */
15624     indexOf : function(node){
15625         node = this.getNode(node);
15626         if(typeof node.nodeIndex == "number"){
15627             return node.nodeIndex;
15628         }
15629         var ns = this.nodes;
15630         for(var i = 0, len = ns.length; i < len; i++){
15631             if(ns[i] == node){
15632                 return i;
15633             }
15634         }
15635         return -1;
15636     }
15637 });
15638 /*
15639  * - LGPL
15640  *
15641  * based on jquery fullcalendar
15642  * 
15643  */
15644
15645 Roo.bootstrap = Roo.bootstrap || {};
15646 /**
15647  * @class Roo.bootstrap.Calendar
15648  * @extends Roo.bootstrap.Component
15649  * Bootstrap Calendar class
15650  * @cfg {Boolean} loadMask (true|false) default false
15651  * @cfg {Object} header generate the user specific header of the calendar, default false
15652
15653  * @constructor
15654  * Create a new Container
15655  * @param {Object} config The config object
15656  */
15657
15658
15659
15660 Roo.bootstrap.Calendar = function(config){
15661     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15662      this.addEvents({
15663         /**
15664              * @event select
15665              * Fires when a date is selected
15666              * @param {DatePicker} this
15667              * @param {Date} date The selected date
15668              */
15669         'select': true,
15670         /**
15671              * @event monthchange
15672              * Fires when the displayed month changes 
15673              * @param {DatePicker} this
15674              * @param {Date} date The selected month
15675              */
15676         'monthchange': true,
15677         /**
15678              * @event evententer
15679              * Fires when mouse over an event
15680              * @param {Calendar} this
15681              * @param {event} Event
15682              */
15683         'evententer': true,
15684         /**
15685              * @event eventleave
15686              * Fires when the mouse leaves an
15687              * @param {Calendar} this
15688              * @param {event}
15689              */
15690         'eventleave': true,
15691         /**
15692              * @event eventclick
15693              * Fires when the mouse click an
15694              * @param {Calendar} this
15695              * @param {event}
15696              */
15697         'eventclick': true
15698         
15699     });
15700
15701 };
15702
15703 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15704     
15705      /**
15706      * @cfg {Number} startDay
15707      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15708      */
15709     startDay : 0,
15710     
15711     loadMask : false,
15712     
15713     header : false,
15714       
15715     getAutoCreate : function(){
15716         
15717         
15718         var fc_button = function(name, corner, style, content ) {
15719             return Roo.apply({},{
15720                 tag : 'span',
15721                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15722                          (corner.length ?
15723                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15724                             ''
15725                         ),
15726                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15727                 unselectable: 'on'
15728             });
15729         };
15730         
15731         var header = {};
15732         
15733         if(!this.header){
15734             header = {
15735                 tag : 'table',
15736                 cls : 'fc-header',
15737                 style : 'width:100%',
15738                 cn : [
15739                     {
15740                         tag: 'tr',
15741                         cn : [
15742                             {
15743                                 tag : 'td',
15744                                 cls : 'fc-header-left',
15745                                 cn : [
15746                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15747                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15748                                     { tag: 'span', cls: 'fc-header-space' },
15749                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15750
15751
15752                                 ]
15753                             },
15754
15755                             {
15756                                 tag : 'td',
15757                                 cls : 'fc-header-center',
15758                                 cn : [
15759                                     {
15760                                         tag: 'span',
15761                                         cls: 'fc-header-title',
15762                                         cn : {
15763                                             tag: 'H2',
15764                                             html : 'month / year'
15765                                         }
15766                                     }
15767
15768                                 ]
15769                             },
15770                             {
15771                                 tag : 'td',
15772                                 cls : 'fc-header-right',
15773                                 cn : [
15774                               /*      fc_button('month', 'left', '', 'month' ),
15775                                     fc_button('week', '', '', 'week' ),
15776                                     fc_button('day', 'right', '', 'day' )
15777                                 */    
15778
15779                                 ]
15780                             }
15781
15782                         ]
15783                     }
15784                 ]
15785             };
15786         }
15787         
15788         header = this.header;
15789         
15790        
15791         var cal_heads = function() {
15792             var ret = [];
15793             // fixme - handle this.
15794             
15795             for (var i =0; i < Date.dayNames.length; i++) {
15796                 var d = Date.dayNames[i];
15797                 ret.push({
15798                     tag: 'th',
15799                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15800                     html : d.substring(0,3)
15801                 });
15802                 
15803             }
15804             ret[0].cls += ' fc-first';
15805             ret[6].cls += ' fc-last';
15806             return ret;
15807         };
15808         var cal_cell = function(n) {
15809             return  {
15810                 tag: 'td',
15811                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15812                 cn : [
15813                     {
15814                         cn : [
15815                             {
15816                                 cls: 'fc-day-number',
15817                                 html: 'D'
15818                             },
15819                             {
15820                                 cls: 'fc-day-content',
15821                              
15822                                 cn : [
15823                                      {
15824                                         style: 'position: relative;' // height: 17px;
15825                                     }
15826                                 ]
15827                             }
15828                             
15829                             
15830                         ]
15831                     }
15832                 ]
15833                 
15834             }
15835         };
15836         var cal_rows = function() {
15837             
15838             var ret = [];
15839             for (var r = 0; r < 6; r++) {
15840                 var row= {
15841                     tag : 'tr',
15842                     cls : 'fc-week',
15843                     cn : []
15844                 };
15845                 
15846                 for (var i =0; i < Date.dayNames.length; i++) {
15847                     var d = Date.dayNames[i];
15848                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15849
15850                 }
15851                 row.cn[0].cls+=' fc-first';
15852                 row.cn[0].cn[0].style = 'min-height:90px';
15853                 row.cn[6].cls+=' fc-last';
15854                 ret.push(row);
15855                 
15856             }
15857             ret[0].cls += ' fc-first';
15858             ret[4].cls += ' fc-prev-last';
15859             ret[5].cls += ' fc-last';
15860             return ret;
15861             
15862         };
15863         
15864         var cal_table = {
15865             tag: 'table',
15866             cls: 'fc-border-separate',
15867             style : 'width:100%',
15868             cellspacing  : 0,
15869             cn : [
15870                 { 
15871                     tag: 'thead',
15872                     cn : [
15873                         { 
15874                             tag: 'tr',
15875                             cls : 'fc-first fc-last',
15876                             cn : cal_heads()
15877                         }
15878                     ]
15879                 },
15880                 { 
15881                     tag: 'tbody',
15882                     cn : cal_rows()
15883                 }
15884                   
15885             ]
15886         };
15887          
15888          var cfg = {
15889             cls : 'fc fc-ltr',
15890             cn : [
15891                 header,
15892                 {
15893                     cls : 'fc-content',
15894                     style : "position: relative;",
15895                     cn : [
15896                         {
15897                             cls : 'fc-view fc-view-month fc-grid',
15898                             style : 'position: relative',
15899                             unselectable : 'on',
15900                             cn : [
15901                                 {
15902                                     cls : 'fc-event-container',
15903                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15904                                 },
15905                                 cal_table
15906                             ]
15907                         }
15908                     ]
15909     
15910                 }
15911            ] 
15912             
15913         };
15914         
15915          
15916         
15917         return cfg;
15918     },
15919     
15920     
15921     initEvents : function()
15922     {
15923         if(!this.store){
15924             throw "can not find store for calendar";
15925         }
15926         
15927         var mark = {
15928             tag: "div",
15929             cls:"x-dlg-mask",
15930             style: "text-align:center",
15931             cn: [
15932                 {
15933                     tag: "div",
15934                     style: "background-color:white;width:50%;margin:250 auto",
15935                     cn: [
15936                         {
15937                             tag: "img",
15938                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15939                         },
15940                         {
15941                             tag: "span",
15942                             html: "Loading"
15943                         }
15944                         
15945                     ]
15946                 }
15947             ]
15948         };
15949         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15950         
15951         var size = this.el.select('.fc-content', true).first().getSize();
15952         this.maskEl.setSize(size.width, size.height);
15953         this.maskEl.enableDisplayMode("block");
15954         if(!this.loadMask){
15955             this.maskEl.hide();
15956         }
15957         
15958         this.store = Roo.factory(this.store, Roo.data);
15959         this.store.on('load', this.onLoad, this);
15960         this.store.on('beforeload', this.onBeforeLoad, this);
15961         
15962         this.resize();
15963         
15964         this.cells = this.el.select('.fc-day',true);
15965         //Roo.log(this.cells);
15966         this.textNodes = this.el.query('.fc-day-number');
15967         this.cells.addClassOnOver('fc-state-hover');
15968         
15969         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15970         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15971         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15972         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15973         
15974         this.on('monthchange', this.onMonthChange, this);
15975         
15976         this.update(new Date().clearTime());
15977     },
15978     
15979     resize : function() {
15980         var sz  = this.el.getSize();
15981         
15982         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15983         this.el.select('.fc-day-content div',true).setHeight(34);
15984     },
15985     
15986     
15987     // private
15988     showPrevMonth : function(e){
15989         this.update(this.activeDate.add("mo", -1));
15990     },
15991     showToday : function(e){
15992         this.update(new Date().clearTime());
15993     },
15994     // private
15995     showNextMonth : function(e){
15996         this.update(this.activeDate.add("mo", 1));
15997     },
15998
15999     // private
16000     showPrevYear : function(){
16001         this.update(this.activeDate.add("y", -1));
16002     },
16003
16004     // private
16005     showNextYear : function(){
16006         this.update(this.activeDate.add("y", 1));
16007     },
16008
16009     
16010    // private
16011     update : function(date)
16012     {
16013         var vd = this.activeDate;
16014         this.activeDate = date;
16015 //        if(vd && this.el){
16016 //            var t = date.getTime();
16017 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16018 //                Roo.log('using add remove');
16019 //                
16020 //                this.fireEvent('monthchange', this, date);
16021 //                
16022 //                this.cells.removeClass("fc-state-highlight");
16023 //                this.cells.each(function(c){
16024 //                   if(c.dateValue == t){
16025 //                       c.addClass("fc-state-highlight");
16026 //                       setTimeout(function(){
16027 //                            try{c.dom.firstChild.focus();}catch(e){}
16028 //                       }, 50);
16029 //                       return false;
16030 //                   }
16031 //                   return true;
16032 //                });
16033 //                return;
16034 //            }
16035 //        }
16036         
16037         var days = date.getDaysInMonth();
16038         
16039         var firstOfMonth = date.getFirstDateOfMonth();
16040         var startingPos = firstOfMonth.getDay()-this.startDay;
16041         
16042         if(startingPos < this.startDay){
16043             startingPos += 7;
16044         }
16045         
16046         var pm = date.add(Date.MONTH, -1);
16047         var prevStart = pm.getDaysInMonth()-startingPos;
16048 //        
16049         this.cells = this.el.select('.fc-day',true);
16050         this.textNodes = this.el.query('.fc-day-number');
16051         this.cells.addClassOnOver('fc-state-hover');
16052         
16053         var cells = this.cells.elements;
16054         var textEls = this.textNodes;
16055         
16056         Roo.each(cells, function(cell){
16057             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16058         });
16059         
16060         days += startingPos;
16061
16062         // convert everything to numbers so it's fast
16063         var day = 86400000;
16064         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16065         //Roo.log(d);
16066         //Roo.log(pm);
16067         //Roo.log(prevStart);
16068         
16069         var today = new Date().clearTime().getTime();
16070         var sel = date.clearTime().getTime();
16071         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16072         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16073         var ddMatch = this.disabledDatesRE;
16074         var ddText = this.disabledDatesText;
16075         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16076         var ddaysText = this.disabledDaysText;
16077         var format = this.format;
16078         
16079         var setCellClass = function(cal, cell){
16080             cell.row = 0;
16081             cell.events = [];
16082             cell.more = [];
16083             //Roo.log('set Cell Class');
16084             cell.title = "";
16085             var t = d.getTime();
16086             
16087             //Roo.log(d);
16088             
16089             cell.dateValue = t;
16090             if(t == today){
16091                 cell.className += " fc-today";
16092                 cell.className += " fc-state-highlight";
16093                 cell.title = cal.todayText;
16094             }
16095             if(t == sel){
16096                 // disable highlight in other month..
16097                 //cell.className += " fc-state-highlight";
16098                 
16099             }
16100             // disabling
16101             if(t < min) {
16102                 cell.className = " fc-state-disabled";
16103                 cell.title = cal.minText;
16104                 return;
16105             }
16106             if(t > max) {
16107                 cell.className = " fc-state-disabled";
16108                 cell.title = cal.maxText;
16109                 return;
16110             }
16111             if(ddays){
16112                 if(ddays.indexOf(d.getDay()) != -1){
16113                     cell.title = ddaysText;
16114                     cell.className = " fc-state-disabled";
16115                 }
16116             }
16117             if(ddMatch && format){
16118                 var fvalue = d.dateFormat(format);
16119                 if(ddMatch.test(fvalue)){
16120                     cell.title = ddText.replace("%0", fvalue);
16121                     cell.className = " fc-state-disabled";
16122                 }
16123             }
16124             
16125             if (!cell.initialClassName) {
16126                 cell.initialClassName = cell.dom.className;
16127             }
16128             
16129             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16130         };
16131
16132         var i = 0;
16133         
16134         for(; i < startingPos; i++) {
16135             textEls[i].innerHTML = (++prevStart);
16136             d.setDate(d.getDate()+1);
16137             
16138             cells[i].className = "fc-past fc-other-month";
16139             setCellClass(this, cells[i]);
16140         }
16141         
16142         var intDay = 0;
16143         
16144         for(; i < days; i++){
16145             intDay = i - startingPos + 1;
16146             textEls[i].innerHTML = (intDay);
16147             d.setDate(d.getDate()+1);
16148             
16149             cells[i].className = ''; // "x-date-active";
16150             setCellClass(this, cells[i]);
16151         }
16152         var extraDays = 0;
16153         
16154         for(; i < 42; i++) {
16155             textEls[i].innerHTML = (++extraDays);
16156             d.setDate(d.getDate()+1);
16157             
16158             cells[i].className = "fc-future fc-other-month";
16159             setCellClass(this, cells[i]);
16160         }
16161         
16162         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16163         
16164         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16165         
16166         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16167         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16168         
16169         if(totalRows != 6){
16170             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16171             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16172         }
16173         
16174         this.fireEvent('monthchange', this, date);
16175         
16176         
16177         /*
16178         if(!this.internalRender){
16179             var main = this.el.dom.firstChild;
16180             var w = main.offsetWidth;
16181             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16182             Roo.fly(main).setWidth(w);
16183             this.internalRender = true;
16184             // opera does not respect the auto grow header center column
16185             // then, after it gets a width opera refuses to recalculate
16186             // without a second pass
16187             if(Roo.isOpera && !this.secondPass){
16188                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16189                 this.secondPass = true;
16190                 this.update.defer(10, this, [date]);
16191             }
16192         }
16193         */
16194         
16195     },
16196     
16197     findCell : function(dt) {
16198         dt = dt.clearTime().getTime();
16199         var ret = false;
16200         this.cells.each(function(c){
16201             //Roo.log("check " +c.dateValue + '?=' + dt);
16202             if(c.dateValue == dt){
16203                 ret = c;
16204                 return false;
16205             }
16206             return true;
16207         });
16208         
16209         return ret;
16210     },
16211     
16212     findCells : function(ev) {
16213         var s = ev.start.clone().clearTime().getTime();
16214        // Roo.log(s);
16215         var e= ev.end.clone().clearTime().getTime();
16216        // Roo.log(e);
16217         var ret = [];
16218         this.cells.each(function(c){
16219              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16220             
16221             if(c.dateValue > e){
16222                 return ;
16223             }
16224             if(c.dateValue < s){
16225                 return ;
16226             }
16227             ret.push(c);
16228         });
16229         
16230         return ret;    
16231     },
16232     
16233 //    findBestRow: function(cells)
16234 //    {
16235 //        var ret = 0;
16236 //        
16237 //        for (var i =0 ; i < cells.length;i++) {
16238 //            ret  = Math.max(cells[i].rows || 0,ret);
16239 //        }
16240 //        return ret;
16241 //        
16242 //    },
16243     
16244     
16245     addItem : function(ev)
16246     {
16247         // look for vertical location slot in
16248         var cells = this.findCells(ev);
16249         
16250 //        ev.row = this.findBestRow(cells);
16251         
16252         // work out the location.
16253         
16254         var crow = false;
16255         var rows = [];
16256         for(var i =0; i < cells.length; i++) {
16257             
16258             cells[i].row = cells[0].row;
16259             
16260             if(i == 0){
16261                 cells[i].row = cells[i].row + 1;
16262             }
16263             
16264             if (!crow) {
16265                 crow = {
16266                     start : cells[i],
16267                     end :  cells[i]
16268                 };
16269                 continue;
16270             }
16271             if (crow.start.getY() == cells[i].getY()) {
16272                 // on same row.
16273                 crow.end = cells[i];
16274                 continue;
16275             }
16276             // different row.
16277             rows.push(crow);
16278             crow = {
16279                 start: cells[i],
16280                 end : cells[i]
16281             };
16282             
16283         }
16284         
16285         rows.push(crow);
16286         ev.els = [];
16287         ev.rows = rows;
16288         ev.cells = cells;
16289         
16290         cells[0].events.push(ev);
16291         
16292         this.calevents.push(ev);
16293     },
16294     
16295     clearEvents: function() {
16296         
16297         if(!this.calevents){
16298             return;
16299         }
16300         
16301         Roo.each(this.cells.elements, function(c){
16302             c.row = 0;
16303             c.events = [];
16304             c.more = [];
16305         });
16306         
16307         Roo.each(this.calevents, function(e) {
16308             Roo.each(e.els, function(el) {
16309                 el.un('mouseenter' ,this.onEventEnter, this);
16310                 el.un('mouseleave' ,this.onEventLeave, this);
16311                 el.remove();
16312             },this);
16313         },this);
16314         
16315         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16316             e.remove();
16317         });
16318         
16319     },
16320     
16321     renderEvents: function()
16322     {   
16323         var _this = this;
16324         
16325         this.cells.each(function(c) {
16326             
16327             if(c.row < 5){
16328                 return;
16329             }
16330             
16331             var ev = c.events;
16332             
16333             var r = 4;
16334             if(c.row != c.events.length){
16335                 r = 4 - (4 - (c.row - c.events.length));
16336             }
16337             
16338             c.events = ev.slice(0, r);
16339             c.more = ev.slice(r);
16340             
16341             if(c.more.length && c.more.length == 1){
16342                 c.events.push(c.more.pop());
16343             }
16344             
16345             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16346             
16347         });
16348             
16349         this.cells.each(function(c) {
16350             
16351             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16352             
16353             
16354             for (var e = 0; e < c.events.length; e++){
16355                 var ev = c.events[e];
16356                 var rows = ev.rows;
16357                 
16358                 for(var i = 0; i < rows.length; i++) {
16359                 
16360                     // how many rows should it span..
16361
16362                     var  cfg = {
16363                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16364                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16365
16366                         unselectable : "on",
16367                         cn : [
16368                             {
16369                                 cls: 'fc-event-inner',
16370                                 cn : [
16371     //                                {
16372     //                                  tag:'span',
16373     //                                  cls: 'fc-event-time',
16374     //                                  html : cells.length > 1 ? '' : ev.time
16375     //                                },
16376                                     {
16377                                       tag:'span',
16378                                       cls: 'fc-event-title',
16379                                       html : String.format('{0}', ev.title)
16380                                     }
16381
16382
16383                                 ]
16384                             },
16385                             {
16386                                 cls: 'ui-resizable-handle ui-resizable-e',
16387                                 html : '&nbsp;&nbsp;&nbsp'
16388                             }
16389
16390                         ]
16391                     };
16392
16393                     if (i == 0) {
16394                         cfg.cls += ' fc-event-start';
16395                     }
16396                     if ((i+1) == rows.length) {
16397                         cfg.cls += ' fc-event-end';
16398                     }
16399
16400                     var ctr = _this.el.select('.fc-event-container',true).first();
16401                     var cg = ctr.createChild(cfg);
16402
16403                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16404                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16405
16406                     var r = (c.more.length) ? 1 : 0;
16407                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16408                     cg.setWidth(ebox.right - sbox.x -2);
16409
16410                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16411                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16412                     cg.on('click', _this.onEventClick, _this, ev);
16413
16414                     ev.els.push(cg);
16415                     
16416                 }
16417                 
16418             }
16419             
16420             
16421             if(c.more.length){
16422                 var  cfg = {
16423                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16424                     style : 'position: absolute',
16425                     unselectable : "on",
16426                     cn : [
16427                         {
16428                             cls: 'fc-event-inner',
16429                             cn : [
16430                                 {
16431                                   tag:'span',
16432                                   cls: 'fc-event-title',
16433                                   html : 'More'
16434                                 }
16435
16436
16437                             ]
16438                         },
16439                         {
16440                             cls: 'ui-resizable-handle ui-resizable-e',
16441                             html : '&nbsp;&nbsp;&nbsp'
16442                         }
16443
16444                     ]
16445                 };
16446
16447                 var ctr = _this.el.select('.fc-event-container',true).first();
16448                 var cg = ctr.createChild(cfg);
16449
16450                 var sbox = c.select('.fc-day-content',true).first().getBox();
16451                 var ebox = c.select('.fc-day-content',true).first().getBox();
16452                 //Roo.log(cg);
16453                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16454                 cg.setWidth(ebox.right - sbox.x -2);
16455
16456                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16457                 
16458             }
16459             
16460         });
16461         
16462         
16463         
16464     },
16465     
16466     onEventEnter: function (e, el,event,d) {
16467         this.fireEvent('evententer', this, el, event);
16468     },
16469     
16470     onEventLeave: function (e, el,event,d) {
16471         this.fireEvent('eventleave', this, el, event);
16472     },
16473     
16474     onEventClick: function (e, el,event,d) {
16475         this.fireEvent('eventclick', this, el, event);
16476     },
16477     
16478     onMonthChange: function () {
16479         this.store.load();
16480     },
16481     
16482     onMoreEventClick: function(e, el, more)
16483     {
16484         var _this = this;
16485         
16486         this.calpopover.placement = 'right';
16487         this.calpopover.setTitle('More');
16488         
16489         this.calpopover.setContent('');
16490         
16491         var ctr = this.calpopover.el.select('.popover-content', true).first();
16492         
16493         Roo.each(more, function(m){
16494             var cfg = {
16495                 cls : 'fc-event-hori fc-event-draggable',
16496                 html : m.title
16497             };
16498             var cg = ctr.createChild(cfg);
16499             
16500             cg.on('click', _this.onEventClick, _this, m);
16501         });
16502         
16503         this.calpopover.show(el);
16504         
16505         
16506     },
16507     
16508     onLoad: function () 
16509     {   
16510         this.calevents = [];
16511         var cal = this;
16512         
16513         if(this.store.getCount() > 0){
16514             this.store.data.each(function(d){
16515                cal.addItem({
16516                     id : d.data.id,
16517                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16518                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16519                     time : d.data.start_time,
16520                     title : d.data.title,
16521                     description : d.data.description,
16522                     venue : d.data.venue
16523                 });
16524             });
16525         }
16526         
16527         this.renderEvents();
16528         
16529         if(this.calevents.length && this.loadMask){
16530             this.maskEl.hide();
16531         }
16532     },
16533     
16534     onBeforeLoad: function()
16535     {
16536         this.clearEvents();
16537         if(this.loadMask){
16538             this.maskEl.show();
16539         }
16540     }
16541 });
16542
16543  
16544  /*
16545  * - LGPL
16546  *
16547  * element
16548  * 
16549  */
16550
16551 /**
16552  * @class Roo.bootstrap.Popover
16553  * @extends Roo.bootstrap.Component
16554  * Bootstrap Popover class
16555  * @cfg {String} html contents of the popover   (or false to use children..)
16556  * @cfg {String} title of popover (or false to hide)
16557  * @cfg {String} placement how it is placed
16558  * @cfg {String} trigger click || hover (or false to trigger manually)
16559  * @cfg {String} over what (parent or false to trigger manually.)
16560  * @cfg {Number} delay - delay before showing
16561  
16562  * @constructor
16563  * Create a new Popover
16564  * @param {Object} config The config object
16565  */
16566
16567 Roo.bootstrap.Popover = function(config){
16568     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16569     
16570     this.addEvents({
16571         // raw events
16572          /**
16573          * @event show
16574          * After the popover show
16575          * 
16576          * @param {Roo.bootstrap.Popover} this
16577          */
16578         "show" : true,
16579         /**
16580          * @event hide
16581          * After the popover hide
16582          * 
16583          * @param {Roo.bootstrap.Popover} this
16584          */
16585         "hide" : true
16586     });
16587 };
16588
16589 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16590     
16591     title: 'Fill in a title',
16592     html: false,
16593     
16594     placement : 'right',
16595     trigger : 'hover', // hover
16596     
16597     delay : 0,
16598     
16599     over: 'parent',
16600     
16601     can_build_overlaid : false,
16602     
16603     getChildContainer : function()
16604     {
16605         return this.el.select('.popover-content',true).first();
16606     },
16607     
16608     getAutoCreate : function(){
16609          
16610         var cfg = {
16611            cls : 'popover roo-dynamic',
16612            style: 'display:block',
16613            cn : [
16614                 {
16615                     cls : 'arrow'
16616                 },
16617                 {
16618                     cls : 'popover-inner',
16619                     cn : [
16620                         {
16621                             tag: 'h3',
16622                             cls: 'popover-title',
16623                             html : this.title
16624                         },
16625                         {
16626                             cls : 'popover-content',
16627                             html : this.html
16628                         }
16629                     ]
16630                     
16631                 }
16632            ]
16633         };
16634         
16635         return cfg;
16636     },
16637     setTitle: function(str)
16638     {
16639         this.title = str;
16640         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16641     },
16642     setContent: function(str)
16643     {
16644         this.html = str;
16645         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16646     },
16647     // as it get's added to the bottom of the page.
16648     onRender : function(ct, position)
16649     {
16650         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16651         if(!this.el){
16652             var cfg = Roo.apply({},  this.getAutoCreate());
16653             cfg.id = Roo.id();
16654             
16655             if (this.cls) {
16656                 cfg.cls += ' ' + this.cls;
16657             }
16658             if (this.style) {
16659                 cfg.style = this.style;
16660             }
16661             //Roo.log("adding to ");
16662             this.el = Roo.get(document.body).createChild(cfg, position);
16663 //            Roo.log(this.el);
16664         }
16665         this.initEvents();
16666     },
16667     
16668     initEvents : function()
16669     {
16670         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16671         this.el.enableDisplayMode('block');
16672         this.el.hide();
16673         if (this.over === false) {
16674             return; 
16675         }
16676         if (this.triggers === false) {
16677             return;
16678         }
16679         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16680         var triggers = this.trigger ? this.trigger.split(' ') : [];
16681         Roo.each(triggers, function(trigger) {
16682         
16683             if (trigger == 'click') {
16684                 on_el.on('click', this.toggle, this);
16685             } else if (trigger != 'manual') {
16686                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16687                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16688       
16689                 on_el.on(eventIn  ,this.enter, this);
16690                 on_el.on(eventOut, this.leave, this);
16691             }
16692         }, this);
16693         
16694     },
16695     
16696     
16697     // private
16698     timeout : null,
16699     hoverState : null,
16700     
16701     toggle : function () {
16702         this.hoverState == 'in' ? this.leave() : this.enter();
16703     },
16704     
16705     enter : function () {
16706         
16707         clearTimeout(this.timeout);
16708     
16709         this.hoverState = 'in';
16710     
16711         if (!this.delay || !this.delay.show) {
16712             this.show();
16713             return;
16714         }
16715         var _t = this;
16716         this.timeout = setTimeout(function () {
16717             if (_t.hoverState == 'in') {
16718                 _t.show();
16719             }
16720         }, this.delay.show)
16721     },
16722     
16723     leave : function() {
16724         clearTimeout(this.timeout);
16725     
16726         this.hoverState = 'out';
16727     
16728         if (!this.delay || !this.delay.hide) {
16729             this.hide();
16730             return;
16731         }
16732         var _t = this;
16733         this.timeout = setTimeout(function () {
16734             if (_t.hoverState == 'out') {
16735                 _t.hide();
16736             }
16737         }, this.delay.hide)
16738     },
16739     
16740     show : function (on_el)
16741     {
16742         if (!on_el) {
16743             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16744         }
16745         
16746         // set content.
16747         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16748         if (this.html !== false) {
16749             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16750         }
16751         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16752         if (!this.title.length) {
16753             this.el.select('.popover-title',true).hide();
16754         }
16755         
16756         var placement = typeof this.placement == 'function' ?
16757             this.placement.call(this, this.el, on_el) :
16758             this.placement;
16759             
16760         var autoToken = /\s?auto?\s?/i;
16761         var autoPlace = autoToken.test(placement);
16762         if (autoPlace) {
16763             placement = placement.replace(autoToken, '') || 'top';
16764         }
16765         
16766         //this.el.detach()
16767         //this.el.setXY([0,0]);
16768         this.el.show();
16769         this.el.dom.style.display='block';
16770         this.el.addClass(placement);
16771         
16772         //this.el.appendTo(on_el);
16773         
16774         var p = this.getPosition();
16775         var box = this.el.getBox();
16776         
16777         if (autoPlace) {
16778             // fixme..
16779         }
16780         var align = Roo.bootstrap.Popover.alignment[placement];
16781         this.el.alignTo(on_el, align[0],align[1]);
16782         //var arrow = this.el.select('.arrow',true).first();
16783         //arrow.set(align[2], 
16784         
16785         this.el.addClass('in');
16786         
16787         
16788         if (this.el.hasClass('fade')) {
16789             // fade it?
16790         }
16791         
16792         this.hoverState = 'in';
16793         
16794         this.fireEvent('show', this);
16795         
16796     },
16797     hide : function()
16798     {
16799         this.el.setXY([0,0]);
16800         this.el.removeClass('in');
16801         this.el.hide();
16802         this.hoverState = null;
16803         
16804         this.fireEvent('hide', this);
16805     }
16806     
16807 });
16808
16809 Roo.bootstrap.Popover.alignment = {
16810     'left' : ['r-l', [-10,0], 'right'],
16811     'right' : ['l-r', [10,0], 'left'],
16812     'bottom' : ['t-b', [0,10], 'top'],
16813     'top' : [ 'b-t', [0,-10], 'bottom']
16814 };
16815
16816  /*
16817  * - LGPL
16818  *
16819  * Progress
16820  * 
16821  */
16822
16823 /**
16824  * @class Roo.bootstrap.Progress
16825  * @extends Roo.bootstrap.Component
16826  * Bootstrap Progress class
16827  * @cfg {Boolean} striped striped of the progress bar
16828  * @cfg {Boolean} active animated of the progress bar
16829  * 
16830  * 
16831  * @constructor
16832  * Create a new Progress
16833  * @param {Object} config The config object
16834  */
16835
16836 Roo.bootstrap.Progress = function(config){
16837     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16838 };
16839
16840 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16841     
16842     striped : false,
16843     active: false,
16844     
16845     getAutoCreate : function(){
16846         var cfg = {
16847             tag: 'div',
16848             cls: 'progress'
16849         };
16850         
16851         
16852         if(this.striped){
16853             cfg.cls += ' progress-striped';
16854         }
16855       
16856         if(this.active){
16857             cfg.cls += ' active';
16858         }
16859         
16860         
16861         return cfg;
16862     }
16863    
16864 });
16865
16866  
16867
16868  /*
16869  * - LGPL
16870  *
16871  * ProgressBar
16872  * 
16873  */
16874
16875 /**
16876  * @class Roo.bootstrap.ProgressBar
16877  * @extends Roo.bootstrap.Component
16878  * Bootstrap ProgressBar class
16879  * @cfg {Number} aria_valuenow aria-value now
16880  * @cfg {Number} aria_valuemin aria-value min
16881  * @cfg {Number} aria_valuemax aria-value max
16882  * @cfg {String} label label for the progress bar
16883  * @cfg {String} panel (success | info | warning | danger )
16884  * @cfg {String} role role of the progress bar
16885  * @cfg {String} sr_only text
16886  * 
16887  * 
16888  * @constructor
16889  * Create a new ProgressBar
16890  * @param {Object} config The config object
16891  */
16892
16893 Roo.bootstrap.ProgressBar = function(config){
16894     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16895 };
16896
16897 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16898     
16899     aria_valuenow : 0,
16900     aria_valuemin : 0,
16901     aria_valuemax : 100,
16902     label : false,
16903     panel : false,
16904     role : false,
16905     sr_only: false,
16906     
16907     getAutoCreate : function()
16908     {
16909         
16910         var cfg = {
16911             tag: 'div',
16912             cls: 'progress-bar',
16913             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16914         };
16915         
16916         if(this.sr_only){
16917             cfg.cn = {
16918                 tag: 'span',
16919                 cls: 'sr-only',
16920                 html: this.sr_only
16921             }
16922         }
16923         
16924         if(this.role){
16925             cfg.role = this.role;
16926         }
16927         
16928         if(this.aria_valuenow){
16929             cfg['aria-valuenow'] = this.aria_valuenow;
16930         }
16931         
16932         if(this.aria_valuemin){
16933             cfg['aria-valuemin'] = this.aria_valuemin;
16934         }
16935         
16936         if(this.aria_valuemax){
16937             cfg['aria-valuemax'] = this.aria_valuemax;
16938         }
16939         
16940         if(this.label && !this.sr_only){
16941             cfg.html = this.label;
16942         }
16943         
16944         if(this.panel){
16945             cfg.cls += ' progress-bar-' + this.panel;
16946         }
16947         
16948         return cfg;
16949     },
16950     
16951     update : function(aria_valuenow)
16952     {
16953         this.aria_valuenow = aria_valuenow;
16954         
16955         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16956     }
16957    
16958 });
16959
16960  
16961
16962  /*
16963  * - LGPL
16964  *
16965  * column
16966  * 
16967  */
16968
16969 /**
16970  * @class Roo.bootstrap.TabGroup
16971  * @extends Roo.bootstrap.Column
16972  * Bootstrap Column class
16973  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16974  * @cfg {Boolean} carousel true to make the group behave like a carousel
16975  * @cfg {Boolean} bullets show bullets for the panels
16976  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16977  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16978  * @cfg {Boolean} showarrow (true|false) show arrow default true
16979  * 
16980  * @constructor
16981  * Create a new TabGroup
16982  * @param {Object} config The config object
16983  */
16984
16985 Roo.bootstrap.TabGroup = function(config){
16986     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16987     if (!this.navId) {
16988         this.navId = Roo.id();
16989     }
16990     this.tabs = [];
16991     Roo.bootstrap.TabGroup.register(this);
16992     
16993 };
16994
16995 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16996     
16997     carousel : false,
16998     transition : false,
16999     bullets : 0,
17000     timer : 0,
17001     autoslide : false,
17002     slideFn : false,
17003     slideOnTouch : false,
17004     showarrow : true,
17005     
17006     getAutoCreate : function()
17007     {
17008         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17009         
17010         cfg.cls += ' tab-content';
17011         
17012         if (this.carousel) {
17013             cfg.cls += ' carousel slide';
17014             
17015             cfg.cn = [{
17016                cls : 'carousel-inner',
17017                cn : []
17018             }];
17019         
17020             if(this.bullets  && !Roo.isTouch){
17021                 
17022                 var bullets = {
17023                     cls : 'carousel-bullets',
17024                     cn : []
17025                 };
17026                
17027                 if(this.bullets_cls){
17028                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17029                 }
17030                 
17031                 bullets.cn.push({
17032                     cls : 'clear'
17033                 });
17034                 
17035                 cfg.cn[0].cn.push(bullets);
17036             }
17037             
17038             if(this.showarrow){
17039                 cfg.cn[0].cn.push({
17040                     tag : 'div',
17041                     class : 'carousel-arrow',
17042                     cn : [
17043                         {
17044                             tag : 'div',
17045                             class : 'carousel-prev',
17046                             cn : [
17047                                 {
17048                                     tag : 'i',
17049                                     class : 'fa fa-chevron-left'
17050                                 }
17051                             ]
17052                         },
17053                         {
17054                             tag : 'div',
17055                             class : 'carousel-next',
17056                             cn : [
17057                                 {
17058                                     tag : 'i',
17059                                     class : 'fa fa-chevron-right'
17060                                 }
17061                             ]
17062                         }
17063                     ]
17064                 });
17065             }
17066             
17067         }
17068         
17069         return cfg;
17070     },
17071     
17072     initEvents:  function()
17073     {
17074 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17075 //            this.el.on("touchstart", this.onTouchStart, this);
17076 //        }
17077         
17078         if(this.autoslide){
17079             var _this = this;
17080             
17081             this.slideFn = window.setInterval(function() {
17082                 _this.showPanelNext();
17083             }, this.timer);
17084         }
17085         
17086         if(this.showarrow){
17087             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17088             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17089         }
17090         
17091         
17092     },
17093     
17094 //    onTouchStart : function(e, el, o)
17095 //    {
17096 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17097 //            return;
17098 //        }
17099 //        
17100 //        this.showPanelNext();
17101 //    },
17102     
17103     
17104     getChildContainer : function()
17105     {
17106         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17107     },
17108     
17109     /**
17110     * register a Navigation item
17111     * @param {Roo.bootstrap.NavItem} the navitem to add
17112     */
17113     register : function(item)
17114     {
17115         this.tabs.push( item);
17116         item.navId = this.navId; // not really needed..
17117         this.addBullet();
17118     
17119     },
17120     
17121     getActivePanel : function()
17122     {
17123         var r = false;
17124         Roo.each(this.tabs, function(t) {
17125             if (t.active) {
17126                 r = t;
17127                 return false;
17128             }
17129             return null;
17130         });
17131         return r;
17132         
17133     },
17134     getPanelByName : function(n)
17135     {
17136         var r = false;
17137         Roo.each(this.tabs, function(t) {
17138             if (t.tabId == n) {
17139                 r = t;
17140                 return false;
17141             }
17142             return null;
17143         });
17144         return r;
17145     },
17146     indexOfPanel : function(p)
17147     {
17148         var r = false;
17149         Roo.each(this.tabs, function(t,i) {
17150             if (t.tabId == p.tabId) {
17151                 r = i;
17152                 return false;
17153             }
17154             return null;
17155         });
17156         return r;
17157     },
17158     /**
17159      * show a specific panel
17160      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17161      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17162      */
17163     showPanel : function (pan)
17164     {
17165         if(this.transition || typeof(pan) == 'undefined'){
17166             Roo.log("waiting for the transitionend");
17167             return;
17168         }
17169         
17170         if (typeof(pan) == 'number') {
17171             pan = this.tabs[pan];
17172         }
17173         
17174         if (typeof(pan) == 'string') {
17175             pan = this.getPanelByName(pan);
17176         }
17177         
17178         var cur = this.getActivePanel();
17179         
17180         if(!pan || !cur){
17181             Roo.log('pan or acitve pan is undefined');
17182             return false;
17183         }
17184         
17185         if (pan.tabId == this.getActivePanel().tabId) {
17186             return true;
17187         }
17188         
17189         if (false === cur.fireEvent('beforedeactivate')) {
17190             return false;
17191         }
17192         
17193         if(this.bullets > 0 && !Roo.isTouch){
17194             this.setActiveBullet(this.indexOfPanel(pan));
17195         }
17196         
17197         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17198             
17199             this.transition = true;
17200             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17201             var lr = dir == 'next' ? 'left' : 'right';
17202             pan.el.addClass(dir); // or prev
17203             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17204             cur.el.addClass(lr); // or right
17205             pan.el.addClass(lr);
17206             
17207             var _this = this;
17208             cur.el.on('transitionend', function() {
17209                 Roo.log("trans end?");
17210                 
17211                 pan.el.removeClass([lr,dir]);
17212                 pan.setActive(true);
17213                 
17214                 cur.el.removeClass([lr]);
17215                 cur.setActive(false);
17216                 
17217                 _this.transition = false;
17218                 
17219             }, this, { single:  true } );
17220             
17221             return true;
17222         }
17223         
17224         cur.setActive(false);
17225         pan.setActive(true);
17226         
17227         return true;
17228         
17229     },
17230     showPanelNext : function()
17231     {
17232         var i = this.indexOfPanel(this.getActivePanel());
17233         
17234         if (i >= this.tabs.length - 1 && !this.autoslide) {
17235             return;
17236         }
17237         
17238         if (i >= this.tabs.length - 1 && this.autoslide) {
17239             i = -1;
17240         }
17241         
17242         this.showPanel(this.tabs[i+1]);
17243     },
17244     
17245     showPanelPrev : function()
17246     {
17247         var i = this.indexOfPanel(this.getActivePanel());
17248         
17249         if (i  < 1 && !this.autoslide) {
17250             return;
17251         }
17252         
17253         if (i < 1 && this.autoslide) {
17254             i = this.tabs.length;
17255         }
17256         
17257         this.showPanel(this.tabs[i-1]);
17258     },
17259     
17260     
17261     addBullet: function()
17262     {
17263         if(!this.bullets || Roo.isTouch){
17264             return;
17265         }
17266         var ctr = this.el.select('.carousel-bullets',true).first();
17267         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17268         var bullet = ctr.createChild({
17269             cls : 'bullet bullet-' + i
17270         },ctr.dom.lastChild);
17271         
17272         
17273         var _this = this;
17274         
17275         bullet.on('click', (function(e, el, o, ii, t){
17276
17277             e.preventDefault();
17278
17279             this.showPanel(ii);
17280
17281             if(this.autoslide && this.slideFn){
17282                 clearInterval(this.slideFn);
17283                 this.slideFn = window.setInterval(function() {
17284                     _this.showPanelNext();
17285                 }, this.timer);
17286             }
17287
17288         }).createDelegate(this, [i, bullet], true));
17289                 
17290         
17291     },
17292      
17293     setActiveBullet : function(i)
17294     {
17295         if(Roo.isTouch){
17296             return;
17297         }
17298         
17299         Roo.each(this.el.select('.bullet', true).elements, function(el){
17300             el.removeClass('selected');
17301         });
17302
17303         var bullet = this.el.select('.bullet-' + i, true).first();
17304         
17305         if(!bullet){
17306             return;
17307         }
17308         
17309         bullet.addClass('selected');
17310     }
17311     
17312     
17313   
17314 });
17315
17316  
17317
17318  
17319  
17320 Roo.apply(Roo.bootstrap.TabGroup, {
17321     
17322     groups: {},
17323      /**
17324     * register a Navigation Group
17325     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17326     */
17327     register : function(navgrp)
17328     {
17329         this.groups[navgrp.navId] = navgrp;
17330         
17331     },
17332     /**
17333     * fetch a Navigation Group based on the navigation ID
17334     * if one does not exist , it will get created.
17335     * @param {string} the navgroup to add
17336     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17337     */
17338     get: function(navId) {
17339         if (typeof(this.groups[navId]) == 'undefined') {
17340             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17341         }
17342         return this.groups[navId] ;
17343     }
17344     
17345     
17346     
17347 });
17348
17349  /*
17350  * - LGPL
17351  *
17352  * TabPanel
17353  * 
17354  */
17355
17356 /**
17357  * @class Roo.bootstrap.TabPanel
17358  * @extends Roo.bootstrap.Component
17359  * Bootstrap TabPanel class
17360  * @cfg {Boolean} active panel active
17361  * @cfg {String} html panel content
17362  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17363  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17364  * @cfg {String} href click to link..
17365  * 
17366  * 
17367  * @constructor
17368  * Create a new TabPanel
17369  * @param {Object} config The config object
17370  */
17371
17372 Roo.bootstrap.TabPanel = function(config){
17373     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17374     this.addEvents({
17375         /**
17376              * @event changed
17377              * Fires when the active status changes
17378              * @param {Roo.bootstrap.TabPanel} this
17379              * @param {Boolean} state the new state
17380             
17381          */
17382         'changed': true,
17383         /**
17384              * @event beforedeactivate
17385              * Fires before a tab is de-activated - can be used to do validation on a form.
17386              * @param {Roo.bootstrap.TabPanel} this
17387              * @return {Boolean} false if there is an error
17388             
17389          */
17390         'beforedeactivate': true
17391      });
17392     
17393     this.tabId = this.tabId || Roo.id();
17394   
17395 };
17396
17397 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17398     
17399     active: false,
17400     html: false,
17401     tabId: false,
17402     navId : false,
17403     href : '',
17404     
17405     getAutoCreate : function(){
17406         var cfg = {
17407             tag: 'div',
17408             // item is needed for carousel - not sure if it has any effect otherwise
17409             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17410             html: this.html || ''
17411         };
17412         
17413         if(this.active){
17414             cfg.cls += ' active';
17415         }
17416         
17417         if(this.tabId){
17418             cfg.tabId = this.tabId;
17419         }
17420         
17421         
17422         return cfg;
17423     },
17424     
17425     initEvents:  function()
17426     {
17427         var p = this.parent();
17428         
17429         this.navId = this.navId || p.navId;
17430         
17431         if (typeof(this.navId) != 'undefined') {
17432             // not really needed.. but just in case.. parent should be a NavGroup.
17433             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17434             
17435             tg.register(this);
17436             
17437             var i = tg.tabs.length - 1;
17438             
17439             if(this.active && tg.bullets > 0 && i < tg.bullets){
17440                 tg.setActiveBullet(i);
17441             }
17442         }
17443         
17444         this.el.on('click', this.onClick, this);
17445         
17446         if(Roo.isTouch){
17447             this.el.on("touchstart", this.onTouchStart, this);
17448             this.el.on("touchmove", this.onTouchMove, this);
17449             this.el.on("touchend", this.onTouchEnd, this);
17450         }
17451         
17452     },
17453     
17454     onRender : function(ct, position)
17455     {
17456         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17457     },
17458     
17459     setActive : function(state)
17460     {
17461         Roo.log("panel - set active " + this.tabId + "=" + state);
17462         
17463         this.active = state;
17464         if (!state) {
17465             this.el.removeClass('active');
17466             
17467         } else  if (!this.el.hasClass('active')) {
17468             this.el.addClass('active');
17469         }
17470         
17471         this.fireEvent('changed', this, state);
17472     },
17473     
17474     onClick : function(e)
17475     {
17476         e.preventDefault();
17477         
17478         if(!this.href.length){
17479             return;
17480         }
17481         
17482         window.location.href = this.href;
17483     },
17484     
17485     startX : 0,
17486     startY : 0,
17487     endX : 0,
17488     endY : 0,
17489     swiping : false,
17490     
17491     onTouchStart : function(e)
17492     {
17493         this.swiping = false;
17494         
17495         this.startX = e.browserEvent.touches[0].clientX;
17496         this.startY = e.browserEvent.touches[0].clientY;
17497     },
17498     
17499     onTouchMove : function(e)
17500     {
17501         this.swiping = true;
17502         
17503         this.endX = e.browserEvent.touches[0].clientX;
17504         this.endY = e.browserEvent.touches[0].clientY;
17505     },
17506     
17507     onTouchEnd : function(e)
17508     {
17509         if(!this.swiping){
17510             this.onClick(e);
17511             return;
17512         }
17513         
17514         var tabGroup = this.parent();
17515         
17516         if(this.endX > this.startX){ // swiping right
17517             tabGroup.showPanelPrev();
17518             return;
17519         }
17520         
17521         if(this.startX > this.endX){ // swiping left
17522             tabGroup.showPanelNext();
17523             return;
17524         }
17525     }
17526     
17527     
17528 });
17529  
17530
17531  
17532
17533  /*
17534  * - LGPL
17535  *
17536  * DateField
17537  * 
17538  */
17539
17540 /**
17541  * @class Roo.bootstrap.DateField
17542  * @extends Roo.bootstrap.Input
17543  * Bootstrap DateField class
17544  * @cfg {Number} weekStart default 0
17545  * @cfg {String} viewMode default empty, (months|years)
17546  * @cfg {String} minViewMode default empty, (months|years)
17547  * @cfg {Number} startDate default -Infinity
17548  * @cfg {Number} endDate default Infinity
17549  * @cfg {Boolean} todayHighlight default false
17550  * @cfg {Boolean} todayBtn default false
17551  * @cfg {Boolean} calendarWeeks default false
17552  * @cfg {Object} daysOfWeekDisabled default empty
17553  * @cfg {Boolean} singleMode default false (true | false)
17554  * 
17555  * @cfg {Boolean} keyboardNavigation default true
17556  * @cfg {String} language default en
17557  * 
17558  * @constructor
17559  * Create a new DateField
17560  * @param {Object} config The config object
17561  */
17562
17563 Roo.bootstrap.DateField = function(config){
17564     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17565      this.addEvents({
17566             /**
17567              * @event show
17568              * Fires when this field show.
17569              * @param {Roo.bootstrap.DateField} this
17570              * @param {Mixed} date The date value
17571              */
17572             show : true,
17573             /**
17574              * @event show
17575              * Fires when this field hide.
17576              * @param {Roo.bootstrap.DateField} this
17577              * @param {Mixed} date The date value
17578              */
17579             hide : true,
17580             /**
17581              * @event select
17582              * Fires when select a date.
17583              * @param {Roo.bootstrap.DateField} this
17584              * @param {Mixed} date The date value
17585              */
17586             select : true,
17587             /**
17588              * @event beforeselect
17589              * Fires when before select a date.
17590              * @param {Roo.bootstrap.DateField} this
17591              * @param {Mixed} date The date value
17592              */
17593             beforeselect : true
17594         });
17595 };
17596
17597 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17598     
17599     /**
17600      * @cfg {String} format
17601      * The default date format string which can be overriden for localization support.  The format must be
17602      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17603      */
17604     format : "m/d/y",
17605     /**
17606      * @cfg {String} altFormats
17607      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17608      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17609      */
17610     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17611     
17612     weekStart : 0,
17613     
17614     viewMode : '',
17615     
17616     minViewMode : '',
17617     
17618     todayHighlight : false,
17619     
17620     todayBtn: false,
17621     
17622     language: 'en',
17623     
17624     keyboardNavigation: true,
17625     
17626     calendarWeeks: false,
17627     
17628     startDate: -Infinity,
17629     
17630     endDate: Infinity,
17631     
17632     daysOfWeekDisabled: [],
17633     
17634     _events: [],
17635     
17636     singleMode : false,
17637     
17638     UTCDate: function()
17639     {
17640         return new Date(Date.UTC.apply(Date, arguments));
17641     },
17642     
17643     UTCToday: function()
17644     {
17645         var today = new Date();
17646         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17647     },
17648     
17649     getDate: function() {
17650             var d = this.getUTCDate();
17651             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17652     },
17653     
17654     getUTCDate: function() {
17655             return this.date;
17656     },
17657     
17658     setDate: function(d) {
17659             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17660     },
17661     
17662     setUTCDate: function(d) {
17663             this.date = d;
17664             this.setValue(this.formatDate(this.date));
17665     },
17666         
17667     onRender: function(ct, position)
17668     {
17669         
17670         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17671         
17672         this.language = this.language || 'en';
17673         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17674         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17675         
17676         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17677         this.format = this.format || 'm/d/y';
17678         this.isInline = false;
17679         this.isInput = true;
17680         this.component = this.el.select('.add-on', true).first() || false;
17681         this.component = (this.component && this.component.length === 0) ? false : this.component;
17682         this.hasInput = this.component && this.inputEl().length;
17683         
17684         if (typeof(this.minViewMode === 'string')) {
17685             switch (this.minViewMode) {
17686                 case 'months':
17687                     this.minViewMode = 1;
17688                     break;
17689                 case 'years':
17690                     this.minViewMode = 2;
17691                     break;
17692                 default:
17693                     this.minViewMode = 0;
17694                     break;
17695             }
17696         }
17697         
17698         if (typeof(this.viewMode === 'string')) {
17699             switch (this.viewMode) {
17700                 case 'months':
17701                     this.viewMode = 1;
17702                     break;
17703                 case 'years':
17704                     this.viewMode = 2;
17705                     break;
17706                 default:
17707                     this.viewMode = 0;
17708                     break;
17709             }
17710         }
17711                 
17712         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17713         
17714 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17715         
17716         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17717         
17718         this.picker().on('mousedown', this.onMousedown, this);
17719         this.picker().on('click', this.onClick, this);
17720         
17721         this.picker().addClass('datepicker-dropdown');
17722         
17723         this.startViewMode = this.viewMode;
17724         
17725         if(this.singleMode){
17726             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17727                 v.setVisibilityMode(Roo.Element.DISPLAY);
17728                 v.hide();
17729             });
17730             
17731             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17732                 v.setStyle('width', '189px');
17733             });
17734         }
17735         
17736         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17737             if(!this.calendarWeeks){
17738                 v.remove();
17739                 return;
17740             }
17741             
17742             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17743             v.attr('colspan', function(i, val){
17744                 return parseInt(val) + 1;
17745             });
17746         });
17747                         
17748         
17749         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17750         
17751         this.setStartDate(this.startDate);
17752         this.setEndDate(this.endDate);
17753         
17754         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17755         
17756         this.fillDow();
17757         this.fillMonths();
17758         this.update();
17759         this.showMode();
17760         
17761         if(this.isInline) {
17762             this.show();
17763         }
17764     },
17765     
17766     picker : function()
17767     {
17768         return this.pickerEl;
17769 //        return this.el.select('.datepicker', true).first();
17770     },
17771     
17772     fillDow: function()
17773     {
17774         var dowCnt = this.weekStart;
17775         
17776         var dow = {
17777             tag: 'tr',
17778             cn: [
17779                 
17780             ]
17781         };
17782         
17783         if(this.calendarWeeks){
17784             dow.cn.push({
17785                 tag: 'th',
17786                 cls: 'cw',
17787                 html: '&nbsp;'
17788             })
17789         }
17790         
17791         while (dowCnt < this.weekStart + 7) {
17792             dow.cn.push({
17793                 tag: 'th',
17794                 cls: 'dow',
17795                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17796             });
17797         }
17798         
17799         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17800     },
17801     
17802     fillMonths: function()
17803     {    
17804         var i = 0;
17805         var months = this.picker().select('>.datepicker-months td', true).first();
17806         
17807         months.dom.innerHTML = '';
17808         
17809         while (i < 12) {
17810             var month = {
17811                 tag: 'span',
17812                 cls: 'month',
17813                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17814             };
17815             
17816             months.createChild(month);
17817         }
17818         
17819     },
17820     
17821     update: function()
17822     {
17823         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;
17824         
17825         if (this.date < this.startDate) {
17826             this.viewDate = new Date(this.startDate);
17827         } else if (this.date > this.endDate) {
17828             this.viewDate = new Date(this.endDate);
17829         } else {
17830             this.viewDate = new Date(this.date);
17831         }
17832         
17833         this.fill();
17834     },
17835     
17836     fill: function() 
17837     {
17838         var d = new Date(this.viewDate),
17839                 year = d.getUTCFullYear(),
17840                 month = d.getUTCMonth(),
17841                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17842                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17843                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17844                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17845                 currentDate = this.date && this.date.valueOf(),
17846                 today = this.UTCToday();
17847         
17848         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17849         
17850 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17851         
17852 //        this.picker.select('>tfoot th.today').
17853 //                                              .text(dates[this.language].today)
17854 //                                              .toggle(this.todayBtn !== false);
17855     
17856         this.updateNavArrows();
17857         this.fillMonths();
17858                                                 
17859         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17860         
17861         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17862          
17863         prevMonth.setUTCDate(day);
17864         
17865         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17866         
17867         var nextMonth = new Date(prevMonth);
17868         
17869         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17870         
17871         nextMonth = nextMonth.valueOf();
17872         
17873         var fillMonths = false;
17874         
17875         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17876         
17877         while(prevMonth.valueOf() < nextMonth) {
17878             var clsName = '';
17879             
17880             if (prevMonth.getUTCDay() === this.weekStart) {
17881                 if(fillMonths){
17882                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17883                 }
17884                     
17885                 fillMonths = {
17886                     tag: 'tr',
17887                     cn: []
17888                 };
17889                 
17890                 if(this.calendarWeeks){
17891                     // ISO 8601: First week contains first thursday.
17892                     // ISO also states week starts on Monday, but we can be more abstract here.
17893                     var
17894                     // Start of current week: based on weekstart/current date
17895                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17896                     // Thursday of this week
17897                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17898                     // First Thursday of year, year from thursday
17899                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17900                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17901                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17902                     
17903                     fillMonths.cn.push({
17904                         tag: 'td',
17905                         cls: 'cw',
17906                         html: calWeek
17907                     });
17908                 }
17909             }
17910             
17911             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17912                 clsName += ' old';
17913             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17914                 clsName += ' new';
17915             }
17916             if (this.todayHighlight &&
17917                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17918                 prevMonth.getUTCMonth() == today.getMonth() &&
17919                 prevMonth.getUTCDate() == today.getDate()) {
17920                 clsName += ' today';
17921             }
17922             
17923             if (currentDate && prevMonth.valueOf() === currentDate) {
17924                 clsName += ' active';
17925             }
17926             
17927             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17928                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17929                     clsName += ' disabled';
17930             }
17931             
17932             fillMonths.cn.push({
17933                 tag: 'td',
17934                 cls: 'day ' + clsName,
17935                 html: prevMonth.getDate()
17936             });
17937             
17938             prevMonth.setDate(prevMonth.getDate()+1);
17939         }
17940           
17941         var currentYear = this.date && this.date.getUTCFullYear();
17942         var currentMonth = this.date && this.date.getUTCMonth();
17943         
17944         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17945         
17946         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17947             v.removeClass('active');
17948             
17949             if(currentYear === year && k === currentMonth){
17950                 v.addClass('active');
17951             }
17952             
17953             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17954                 v.addClass('disabled');
17955             }
17956             
17957         });
17958         
17959         
17960         year = parseInt(year/10, 10) * 10;
17961         
17962         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17963         
17964         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17965         
17966         year -= 1;
17967         for (var i = -1; i < 11; i++) {
17968             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17969                 tag: 'span',
17970                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17971                 html: year
17972             });
17973             
17974             year += 1;
17975         }
17976     },
17977     
17978     showMode: function(dir) 
17979     {
17980         if (dir) {
17981             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17982         }
17983         
17984         Roo.each(this.picker().select('>div',true).elements, function(v){
17985             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17986             v.hide();
17987         });
17988         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17989     },
17990     
17991     place: function()
17992     {
17993         if(this.isInline) {
17994             return;
17995         }
17996         
17997         this.picker().removeClass(['bottom', 'top']);
17998         
17999         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18000             /*
18001              * place to the top of element!
18002              *
18003              */
18004             
18005             this.picker().addClass('top');
18006             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18007             
18008             return;
18009         }
18010         
18011         this.picker().addClass('bottom');
18012         
18013         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18014     },
18015     
18016     parseDate : function(value)
18017     {
18018         if(!value || value instanceof Date){
18019             return value;
18020         }
18021         var v = Date.parseDate(value, this.format);
18022         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18023             v = Date.parseDate(value, 'Y-m-d');
18024         }
18025         if(!v && this.altFormats){
18026             if(!this.altFormatsArray){
18027                 this.altFormatsArray = this.altFormats.split("|");
18028             }
18029             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18030                 v = Date.parseDate(value, this.altFormatsArray[i]);
18031             }
18032         }
18033         return v;
18034     },
18035     
18036     formatDate : function(date, fmt)
18037     {   
18038         return (!date || !(date instanceof Date)) ?
18039         date : date.dateFormat(fmt || this.format);
18040     },
18041     
18042     onFocus : function()
18043     {
18044         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18045         this.show();
18046     },
18047     
18048     onBlur : function()
18049     {
18050         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18051         
18052         var d = this.inputEl().getValue();
18053         
18054         this.setValue(d);
18055                 
18056         this.hide();
18057     },
18058     
18059     show : function()
18060     {
18061         this.picker().show();
18062         this.update();
18063         this.place();
18064         
18065         this.fireEvent('show', this, this.date);
18066     },
18067     
18068     hide : function()
18069     {
18070         if(this.isInline) {
18071             return;
18072         }
18073         this.picker().hide();
18074         this.viewMode = this.startViewMode;
18075         this.showMode();
18076         
18077         this.fireEvent('hide', this, this.date);
18078         
18079     },
18080     
18081     onMousedown: function(e)
18082     {
18083         e.stopPropagation();
18084         e.preventDefault();
18085     },
18086     
18087     keyup: function(e)
18088     {
18089         Roo.bootstrap.DateField.superclass.keyup.call(this);
18090         this.update();
18091     },
18092
18093     setValue: function(v)
18094     {
18095         if(this.fireEvent('beforeselect', this, v) !== false){
18096             var d = new Date(this.parseDate(v) ).clearTime();
18097         
18098             if(isNaN(d.getTime())){
18099                 this.date = this.viewDate = '';
18100                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18101                 return;
18102             }
18103
18104             v = this.formatDate(d);
18105
18106             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18107
18108             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18109
18110             this.update();
18111
18112             this.fireEvent('select', this, this.date);
18113         }
18114     },
18115     
18116     getValue: function()
18117     {
18118         return this.formatDate(this.date);
18119     },
18120     
18121     fireKey: function(e)
18122     {
18123         if (!this.picker().isVisible()){
18124             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18125                 this.show();
18126             }
18127             return;
18128         }
18129         
18130         var dateChanged = false,
18131         dir, day, month,
18132         newDate, newViewDate;
18133         
18134         switch(e.keyCode){
18135             case 27: // escape
18136                 this.hide();
18137                 e.preventDefault();
18138                 break;
18139             case 37: // left
18140             case 39: // right
18141                 if (!this.keyboardNavigation) {
18142                     break;
18143                 }
18144                 dir = e.keyCode == 37 ? -1 : 1;
18145                 
18146                 if (e.ctrlKey){
18147                     newDate = this.moveYear(this.date, dir);
18148                     newViewDate = this.moveYear(this.viewDate, dir);
18149                 } else if (e.shiftKey){
18150                     newDate = this.moveMonth(this.date, dir);
18151                     newViewDate = this.moveMonth(this.viewDate, dir);
18152                 } else {
18153                     newDate = new Date(this.date);
18154                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18155                     newViewDate = new Date(this.viewDate);
18156                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18157                 }
18158                 if (this.dateWithinRange(newDate)){
18159                     this.date = newDate;
18160                     this.viewDate = newViewDate;
18161                     this.setValue(this.formatDate(this.date));
18162 //                    this.update();
18163                     e.preventDefault();
18164                     dateChanged = true;
18165                 }
18166                 break;
18167             case 38: // up
18168             case 40: // down
18169                 if (!this.keyboardNavigation) {
18170                     break;
18171                 }
18172                 dir = e.keyCode == 38 ? -1 : 1;
18173                 if (e.ctrlKey){
18174                     newDate = this.moveYear(this.date, dir);
18175                     newViewDate = this.moveYear(this.viewDate, dir);
18176                 } else if (e.shiftKey){
18177                     newDate = this.moveMonth(this.date, dir);
18178                     newViewDate = this.moveMonth(this.viewDate, dir);
18179                 } else {
18180                     newDate = new Date(this.date);
18181                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18182                     newViewDate = new Date(this.viewDate);
18183                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18184                 }
18185                 if (this.dateWithinRange(newDate)){
18186                     this.date = newDate;
18187                     this.viewDate = newViewDate;
18188                     this.setValue(this.formatDate(this.date));
18189 //                    this.update();
18190                     e.preventDefault();
18191                     dateChanged = true;
18192                 }
18193                 break;
18194             case 13: // enter
18195                 this.setValue(this.formatDate(this.date));
18196                 this.hide();
18197                 e.preventDefault();
18198                 break;
18199             case 9: // tab
18200                 this.setValue(this.formatDate(this.date));
18201                 this.hide();
18202                 break;
18203             case 16: // shift
18204             case 17: // ctrl
18205             case 18: // alt
18206                 break;
18207             default :
18208                 this.hide();
18209                 
18210         }
18211     },
18212     
18213     
18214     onClick: function(e) 
18215     {
18216         e.stopPropagation();
18217         e.preventDefault();
18218         
18219         var target = e.getTarget();
18220         
18221         if(target.nodeName.toLowerCase() === 'i'){
18222             target = Roo.get(target).dom.parentNode;
18223         }
18224         
18225         var nodeName = target.nodeName;
18226         var className = target.className;
18227         var html = target.innerHTML;
18228         //Roo.log(nodeName);
18229         
18230         switch(nodeName.toLowerCase()) {
18231             case 'th':
18232                 switch(className) {
18233                     case 'switch':
18234                         this.showMode(1);
18235                         break;
18236                     case 'prev':
18237                     case 'next':
18238                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18239                         switch(this.viewMode){
18240                                 case 0:
18241                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18242                                         break;
18243                                 case 1:
18244                                 case 2:
18245                                         this.viewDate = this.moveYear(this.viewDate, dir);
18246                                         break;
18247                         }
18248                         this.fill();
18249                         break;
18250                     case 'today':
18251                         var date = new Date();
18252                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18253 //                        this.fill()
18254                         this.setValue(this.formatDate(this.date));
18255                         
18256                         this.hide();
18257                         break;
18258                 }
18259                 break;
18260             case 'span':
18261                 if (className.indexOf('disabled') < 0) {
18262                     this.viewDate.setUTCDate(1);
18263                     if (className.indexOf('month') > -1) {
18264                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18265                     } else {
18266                         var year = parseInt(html, 10) || 0;
18267                         this.viewDate.setUTCFullYear(year);
18268                         
18269                     }
18270                     
18271                     if(this.singleMode){
18272                         this.setValue(this.formatDate(this.viewDate));
18273                         this.hide();
18274                         return;
18275                     }
18276                     
18277                     this.showMode(-1);
18278                     this.fill();
18279                 }
18280                 break;
18281                 
18282             case 'td':
18283                 //Roo.log(className);
18284                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18285                     var day = parseInt(html, 10) || 1;
18286                     var year = this.viewDate.getUTCFullYear(),
18287                         month = this.viewDate.getUTCMonth();
18288
18289                     if (className.indexOf('old') > -1) {
18290                         if(month === 0 ){
18291                             month = 11;
18292                             year -= 1;
18293                         }else{
18294                             month -= 1;
18295                         }
18296                     } else if (className.indexOf('new') > -1) {
18297                         if (month == 11) {
18298                             month = 0;
18299                             year += 1;
18300                         } else {
18301                             month += 1;
18302                         }
18303                     }
18304                     //Roo.log([year,month,day]);
18305                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18306                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18307 //                    this.fill();
18308                     //Roo.log(this.formatDate(this.date));
18309                     this.setValue(this.formatDate(this.date));
18310                     this.hide();
18311                 }
18312                 break;
18313         }
18314     },
18315     
18316     setStartDate: function(startDate)
18317     {
18318         this.startDate = startDate || -Infinity;
18319         if (this.startDate !== -Infinity) {
18320             this.startDate = this.parseDate(this.startDate);
18321         }
18322         this.update();
18323         this.updateNavArrows();
18324     },
18325
18326     setEndDate: function(endDate)
18327     {
18328         this.endDate = endDate || Infinity;
18329         if (this.endDate !== Infinity) {
18330             this.endDate = this.parseDate(this.endDate);
18331         }
18332         this.update();
18333         this.updateNavArrows();
18334     },
18335     
18336     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18337     {
18338         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18339         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18340             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18341         }
18342         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18343             return parseInt(d, 10);
18344         });
18345         this.update();
18346         this.updateNavArrows();
18347     },
18348     
18349     updateNavArrows: function() 
18350     {
18351         if(this.singleMode){
18352             return;
18353         }
18354         
18355         var d = new Date(this.viewDate),
18356         year = d.getUTCFullYear(),
18357         month = d.getUTCMonth();
18358         
18359         Roo.each(this.picker().select('.prev', true).elements, function(v){
18360             v.show();
18361             switch (this.viewMode) {
18362                 case 0:
18363
18364                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18365                         v.hide();
18366                     }
18367                     break;
18368                 case 1:
18369                 case 2:
18370                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18371                         v.hide();
18372                     }
18373                     break;
18374             }
18375         });
18376         
18377         Roo.each(this.picker().select('.next', true).elements, function(v){
18378             v.show();
18379             switch (this.viewMode) {
18380                 case 0:
18381
18382                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18383                         v.hide();
18384                     }
18385                     break;
18386                 case 1:
18387                 case 2:
18388                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18389                         v.hide();
18390                     }
18391                     break;
18392             }
18393         })
18394     },
18395     
18396     moveMonth: function(date, dir)
18397     {
18398         if (!dir) {
18399             return date;
18400         }
18401         var new_date = new Date(date.valueOf()),
18402         day = new_date.getUTCDate(),
18403         month = new_date.getUTCMonth(),
18404         mag = Math.abs(dir),
18405         new_month, test;
18406         dir = dir > 0 ? 1 : -1;
18407         if (mag == 1){
18408             test = dir == -1
18409             // If going back one month, make sure month is not current month
18410             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18411             ? function(){
18412                 return new_date.getUTCMonth() == month;
18413             }
18414             // If going forward one month, make sure month is as expected
18415             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18416             : function(){
18417                 return new_date.getUTCMonth() != new_month;
18418             };
18419             new_month = month + dir;
18420             new_date.setUTCMonth(new_month);
18421             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18422             if (new_month < 0 || new_month > 11) {
18423                 new_month = (new_month + 12) % 12;
18424             }
18425         } else {
18426             // For magnitudes >1, move one month at a time...
18427             for (var i=0; i<mag; i++) {
18428                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18429                 new_date = this.moveMonth(new_date, dir);
18430             }
18431             // ...then reset the day, keeping it in the new month
18432             new_month = new_date.getUTCMonth();
18433             new_date.setUTCDate(day);
18434             test = function(){
18435                 return new_month != new_date.getUTCMonth();
18436             };
18437         }
18438         // Common date-resetting loop -- if date is beyond end of month, make it
18439         // end of month
18440         while (test()){
18441             new_date.setUTCDate(--day);
18442             new_date.setUTCMonth(new_month);
18443         }
18444         return new_date;
18445     },
18446
18447     moveYear: function(date, dir)
18448     {
18449         return this.moveMonth(date, dir*12);
18450     },
18451
18452     dateWithinRange: function(date)
18453     {
18454         return date >= this.startDate && date <= this.endDate;
18455     },
18456
18457     
18458     remove: function() 
18459     {
18460         this.picker().remove();
18461     },
18462     
18463     validateValue : function(value)
18464     {
18465         if(value.length < 1)  {
18466             if(this.allowBlank){
18467                 return true;
18468             }
18469             return false;
18470         }
18471         
18472         if(value.length < this.minLength){
18473             return false;
18474         }
18475         if(value.length > this.maxLength){
18476             return false;
18477         }
18478         if(this.vtype){
18479             var vt = Roo.form.VTypes;
18480             if(!vt[this.vtype](value, this)){
18481                 return false;
18482             }
18483         }
18484         if(typeof this.validator == "function"){
18485             var msg = this.validator(value);
18486             if(msg !== true){
18487                 return false;
18488             }
18489         }
18490         
18491         if(this.regex && !this.regex.test(value)){
18492             return false;
18493         }
18494         
18495         if(typeof(this.parseDate(value)) == 'undefined'){
18496             return false;
18497         }
18498         
18499         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18500             return false;
18501         }      
18502         
18503         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18504             return false;
18505         } 
18506         
18507         
18508         return true;
18509     }
18510    
18511 });
18512
18513 Roo.apply(Roo.bootstrap.DateField,  {
18514     
18515     head : {
18516         tag: 'thead',
18517         cn: [
18518         {
18519             tag: 'tr',
18520             cn: [
18521             {
18522                 tag: 'th',
18523                 cls: 'prev',
18524                 html: '<i class="fa fa-arrow-left"/>'
18525             },
18526             {
18527                 tag: 'th',
18528                 cls: 'switch',
18529                 colspan: '5'
18530             },
18531             {
18532                 tag: 'th',
18533                 cls: 'next',
18534                 html: '<i class="fa fa-arrow-right"/>'
18535             }
18536
18537             ]
18538         }
18539         ]
18540     },
18541     
18542     content : {
18543         tag: 'tbody',
18544         cn: [
18545         {
18546             tag: 'tr',
18547             cn: [
18548             {
18549                 tag: 'td',
18550                 colspan: '7'
18551             }
18552             ]
18553         }
18554         ]
18555     },
18556     
18557     footer : {
18558         tag: 'tfoot',
18559         cn: [
18560         {
18561             tag: 'tr',
18562             cn: [
18563             {
18564                 tag: 'th',
18565                 colspan: '7',
18566                 cls: 'today'
18567             }
18568                     
18569             ]
18570         }
18571         ]
18572     },
18573     
18574     dates:{
18575         en: {
18576             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18577             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18578             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18579             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18580             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18581             today: "Today"
18582         }
18583     },
18584     
18585     modes: [
18586     {
18587         clsName: 'days',
18588         navFnc: 'Month',
18589         navStep: 1
18590     },
18591     {
18592         clsName: 'months',
18593         navFnc: 'FullYear',
18594         navStep: 1
18595     },
18596     {
18597         clsName: 'years',
18598         navFnc: 'FullYear',
18599         navStep: 10
18600     }]
18601 });
18602
18603 Roo.apply(Roo.bootstrap.DateField,  {
18604   
18605     template : {
18606         tag: 'div',
18607         cls: 'datepicker dropdown-menu roo-dynamic',
18608         cn: [
18609         {
18610             tag: 'div',
18611             cls: 'datepicker-days',
18612             cn: [
18613             {
18614                 tag: 'table',
18615                 cls: 'table-condensed',
18616                 cn:[
18617                 Roo.bootstrap.DateField.head,
18618                 {
18619                     tag: 'tbody'
18620                 },
18621                 Roo.bootstrap.DateField.footer
18622                 ]
18623             }
18624             ]
18625         },
18626         {
18627             tag: 'div',
18628             cls: 'datepicker-months',
18629             cn: [
18630             {
18631                 tag: 'table',
18632                 cls: 'table-condensed',
18633                 cn:[
18634                 Roo.bootstrap.DateField.head,
18635                 Roo.bootstrap.DateField.content,
18636                 Roo.bootstrap.DateField.footer
18637                 ]
18638             }
18639             ]
18640         },
18641         {
18642             tag: 'div',
18643             cls: 'datepicker-years',
18644             cn: [
18645             {
18646                 tag: 'table',
18647                 cls: 'table-condensed',
18648                 cn:[
18649                 Roo.bootstrap.DateField.head,
18650                 Roo.bootstrap.DateField.content,
18651                 Roo.bootstrap.DateField.footer
18652                 ]
18653             }
18654             ]
18655         }
18656         ]
18657     }
18658 });
18659
18660  
18661
18662  /*
18663  * - LGPL
18664  *
18665  * TimeField
18666  * 
18667  */
18668
18669 /**
18670  * @class Roo.bootstrap.TimeField
18671  * @extends Roo.bootstrap.Input
18672  * Bootstrap DateField class
18673  * 
18674  * 
18675  * @constructor
18676  * Create a new TimeField
18677  * @param {Object} config The config object
18678  */
18679
18680 Roo.bootstrap.TimeField = function(config){
18681     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18682     this.addEvents({
18683             /**
18684              * @event show
18685              * Fires when this field show.
18686              * @param {Roo.bootstrap.DateField} thisthis
18687              * @param {Mixed} date The date value
18688              */
18689             show : true,
18690             /**
18691              * @event show
18692              * Fires when this field hide.
18693              * @param {Roo.bootstrap.DateField} this
18694              * @param {Mixed} date The date value
18695              */
18696             hide : true,
18697             /**
18698              * @event select
18699              * Fires when select a date.
18700              * @param {Roo.bootstrap.DateField} this
18701              * @param {Mixed} date The date value
18702              */
18703             select : true
18704         });
18705 };
18706
18707 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18708     
18709     /**
18710      * @cfg {String} format
18711      * The default time format string which can be overriden for localization support.  The format must be
18712      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18713      */
18714     format : "H:i",
18715        
18716     onRender: function(ct, position)
18717     {
18718         
18719         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18720                 
18721         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18722         
18723         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18724         
18725         this.pop = this.picker().select('>.datepicker-time',true).first();
18726         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18727         
18728         this.picker().on('mousedown', this.onMousedown, this);
18729         this.picker().on('click', this.onClick, this);
18730         
18731         this.picker().addClass('datepicker-dropdown');
18732     
18733         this.fillTime();
18734         this.update();
18735             
18736         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18737         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18738         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18739         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18740         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18741         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18742
18743     },
18744     
18745     fireKey: function(e){
18746         if (!this.picker().isVisible()){
18747             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18748                 this.show();
18749             }
18750             return;
18751         }
18752
18753         e.preventDefault();
18754         
18755         switch(e.keyCode){
18756             case 27: // escape
18757                 this.hide();
18758                 break;
18759             case 37: // left
18760             case 39: // right
18761                 this.onTogglePeriod();
18762                 break;
18763             case 38: // up
18764                 this.onIncrementMinutes();
18765                 break;
18766             case 40: // down
18767                 this.onDecrementMinutes();
18768                 break;
18769             case 13: // enter
18770             case 9: // tab
18771                 this.setTime();
18772                 break;
18773         }
18774     },
18775     
18776     onClick: function(e) {
18777         e.stopPropagation();
18778         e.preventDefault();
18779     },
18780     
18781     picker : function()
18782     {
18783         return this.el.select('.datepicker', true).first();
18784     },
18785     
18786     fillTime: function()
18787     {    
18788         var time = this.pop.select('tbody', true).first();
18789         
18790         time.dom.innerHTML = '';
18791         
18792         time.createChild({
18793             tag: 'tr',
18794             cn: [
18795                 {
18796                     tag: 'td',
18797                     cn: [
18798                         {
18799                             tag: 'a',
18800                             href: '#',
18801                             cls: 'btn',
18802                             cn: [
18803                                 {
18804                                     tag: 'span',
18805                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18806                                 }
18807                             ]
18808                         } 
18809                     ]
18810                 },
18811                 {
18812                     tag: 'td',
18813                     cls: 'separator'
18814                 },
18815                 {
18816                     tag: 'td',
18817                     cn: [
18818                         {
18819                             tag: 'a',
18820                             href: '#',
18821                             cls: 'btn',
18822                             cn: [
18823                                 {
18824                                     tag: 'span',
18825                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18826                                 }
18827                             ]
18828                         }
18829                     ]
18830                 },
18831                 {
18832                     tag: 'td',
18833                     cls: 'separator'
18834                 }
18835             ]
18836         });
18837         
18838         time.createChild({
18839             tag: 'tr',
18840             cn: [
18841                 {
18842                     tag: 'td',
18843                     cn: [
18844                         {
18845                             tag: 'span',
18846                             cls: 'timepicker-hour',
18847                             html: '00'
18848                         }  
18849                     ]
18850                 },
18851                 {
18852                     tag: 'td',
18853                     cls: 'separator',
18854                     html: ':'
18855                 },
18856                 {
18857                     tag: 'td',
18858                     cn: [
18859                         {
18860                             tag: 'span',
18861                             cls: 'timepicker-minute',
18862                             html: '00'
18863                         }  
18864                     ]
18865                 },
18866                 {
18867                     tag: 'td',
18868                     cls: 'separator'
18869                 },
18870                 {
18871                     tag: 'td',
18872                     cn: [
18873                         {
18874                             tag: 'button',
18875                             type: 'button',
18876                             cls: 'btn btn-primary period',
18877                             html: 'AM'
18878                             
18879                         }
18880                     ]
18881                 }
18882             ]
18883         });
18884         
18885         time.createChild({
18886             tag: 'tr',
18887             cn: [
18888                 {
18889                     tag: 'td',
18890                     cn: [
18891                         {
18892                             tag: 'a',
18893                             href: '#',
18894                             cls: 'btn',
18895                             cn: [
18896                                 {
18897                                     tag: 'span',
18898                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18899                                 }
18900                             ]
18901                         }
18902                     ]
18903                 },
18904                 {
18905                     tag: 'td',
18906                     cls: 'separator'
18907                 },
18908                 {
18909                     tag: 'td',
18910                     cn: [
18911                         {
18912                             tag: 'a',
18913                             href: '#',
18914                             cls: 'btn',
18915                             cn: [
18916                                 {
18917                                     tag: 'span',
18918                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18919                                 }
18920                             ]
18921                         }
18922                     ]
18923                 },
18924                 {
18925                     tag: 'td',
18926                     cls: 'separator'
18927                 }
18928             ]
18929         });
18930         
18931     },
18932     
18933     update: function()
18934     {
18935         
18936         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18937         
18938         this.fill();
18939     },
18940     
18941     fill: function() 
18942     {
18943         var hours = this.time.getHours();
18944         var minutes = this.time.getMinutes();
18945         var period = 'AM';
18946         
18947         if(hours > 11){
18948             period = 'PM';
18949         }
18950         
18951         if(hours == 0){
18952             hours = 12;
18953         }
18954         
18955         
18956         if(hours > 12){
18957             hours = hours - 12;
18958         }
18959         
18960         if(hours < 10){
18961             hours = '0' + hours;
18962         }
18963         
18964         if(minutes < 10){
18965             minutes = '0' + minutes;
18966         }
18967         
18968         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18969         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18970         this.pop.select('button', true).first().dom.innerHTML = period;
18971         
18972     },
18973     
18974     place: function()
18975     {   
18976         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18977         
18978         var cls = ['bottom'];
18979         
18980         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18981             cls.pop();
18982             cls.push('top');
18983         }
18984         
18985         cls.push('right');
18986         
18987         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18988             cls.pop();
18989             cls.push('left');
18990         }
18991         
18992         this.picker().addClass(cls.join('-'));
18993         
18994         var _this = this;
18995         
18996         Roo.each(cls, function(c){
18997             if(c == 'bottom'){
18998                 _this.picker().setTop(_this.inputEl().getHeight());
18999                 return;
19000             }
19001             if(c == 'top'){
19002                 _this.picker().setTop(0 - _this.picker().getHeight());
19003                 return;
19004             }
19005             
19006             if(c == 'left'){
19007                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19008                 return;
19009             }
19010             if(c == 'right'){
19011                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19012                 return;
19013             }
19014         });
19015         
19016     },
19017   
19018     onFocus : function()
19019     {
19020         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19021         this.show();
19022     },
19023     
19024     onBlur : function()
19025     {
19026         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19027         this.hide();
19028     },
19029     
19030     show : function()
19031     {
19032         this.picker().show();
19033         this.pop.show();
19034         this.update();
19035         this.place();
19036         
19037         this.fireEvent('show', this, this.date);
19038     },
19039     
19040     hide : function()
19041     {
19042         this.picker().hide();
19043         this.pop.hide();
19044         
19045         this.fireEvent('hide', this, this.date);
19046     },
19047     
19048     setTime : function()
19049     {
19050         this.hide();
19051         this.setValue(this.time.format(this.format));
19052         
19053         this.fireEvent('select', this, this.date);
19054         
19055         
19056     },
19057     
19058     onMousedown: function(e){
19059         e.stopPropagation();
19060         e.preventDefault();
19061     },
19062     
19063     onIncrementHours: function()
19064     {
19065         Roo.log('onIncrementHours');
19066         this.time = this.time.add(Date.HOUR, 1);
19067         this.update();
19068         
19069     },
19070     
19071     onDecrementHours: function()
19072     {
19073         Roo.log('onDecrementHours');
19074         this.time = this.time.add(Date.HOUR, -1);
19075         this.update();
19076     },
19077     
19078     onIncrementMinutes: function()
19079     {
19080         Roo.log('onIncrementMinutes');
19081         this.time = this.time.add(Date.MINUTE, 1);
19082         this.update();
19083     },
19084     
19085     onDecrementMinutes: function()
19086     {
19087         Roo.log('onDecrementMinutes');
19088         this.time = this.time.add(Date.MINUTE, -1);
19089         this.update();
19090     },
19091     
19092     onTogglePeriod: function()
19093     {
19094         Roo.log('onTogglePeriod');
19095         this.time = this.time.add(Date.HOUR, 12);
19096         this.update();
19097     }
19098     
19099    
19100 });
19101
19102 Roo.apply(Roo.bootstrap.TimeField,  {
19103     
19104     content : {
19105         tag: 'tbody',
19106         cn: [
19107             {
19108                 tag: 'tr',
19109                 cn: [
19110                 {
19111                     tag: 'td',
19112                     colspan: '7'
19113                 }
19114                 ]
19115             }
19116         ]
19117     },
19118     
19119     footer : {
19120         tag: 'tfoot',
19121         cn: [
19122             {
19123                 tag: 'tr',
19124                 cn: [
19125                 {
19126                     tag: 'th',
19127                     colspan: '7',
19128                     cls: '',
19129                     cn: [
19130                         {
19131                             tag: 'button',
19132                             cls: 'btn btn-info ok',
19133                             html: 'OK'
19134                         }
19135                     ]
19136                 }
19137
19138                 ]
19139             }
19140         ]
19141     }
19142 });
19143
19144 Roo.apply(Roo.bootstrap.TimeField,  {
19145   
19146     template : {
19147         tag: 'div',
19148         cls: 'datepicker dropdown-menu',
19149         cn: [
19150             {
19151                 tag: 'div',
19152                 cls: 'datepicker-time',
19153                 cn: [
19154                 {
19155                     tag: 'table',
19156                     cls: 'table-condensed',
19157                     cn:[
19158                     Roo.bootstrap.TimeField.content,
19159                     Roo.bootstrap.TimeField.footer
19160                     ]
19161                 }
19162                 ]
19163             }
19164         ]
19165     }
19166 });
19167
19168  
19169
19170  /*
19171  * - LGPL
19172  *
19173  * MonthField
19174  * 
19175  */
19176
19177 /**
19178  * @class Roo.bootstrap.MonthField
19179  * @extends Roo.bootstrap.Input
19180  * Bootstrap MonthField class
19181  * 
19182  * @cfg {String} language default en
19183  * 
19184  * @constructor
19185  * Create a new MonthField
19186  * @param {Object} config The config object
19187  */
19188
19189 Roo.bootstrap.MonthField = function(config){
19190     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19191     
19192     this.addEvents({
19193         /**
19194          * @event show
19195          * Fires when this field show.
19196          * @param {Roo.bootstrap.MonthField} this
19197          * @param {Mixed} date The date value
19198          */
19199         show : true,
19200         /**
19201          * @event show
19202          * Fires when this field hide.
19203          * @param {Roo.bootstrap.MonthField} this
19204          * @param {Mixed} date The date value
19205          */
19206         hide : true,
19207         /**
19208          * @event select
19209          * Fires when select a date.
19210          * @param {Roo.bootstrap.MonthField} this
19211          * @param {String} oldvalue The old value
19212          * @param {String} newvalue The new value
19213          */
19214         select : true
19215     });
19216 };
19217
19218 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19219     
19220     onRender: function(ct, position)
19221     {
19222         
19223         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19224         
19225         this.language = this.language || 'en';
19226         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19227         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19228         
19229         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19230         this.isInline = false;
19231         this.isInput = true;
19232         this.component = this.el.select('.add-on', true).first() || false;
19233         this.component = (this.component && this.component.length === 0) ? false : this.component;
19234         this.hasInput = this.component && this.inputEL().length;
19235         
19236         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19237         
19238         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19239         
19240         this.picker().on('mousedown', this.onMousedown, this);
19241         this.picker().on('click', this.onClick, this);
19242         
19243         this.picker().addClass('datepicker-dropdown');
19244         
19245         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19246             v.setStyle('width', '189px');
19247         });
19248         
19249         this.fillMonths();
19250         
19251         this.update();
19252         
19253         if(this.isInline) {
19254             this.show();
19255         }
19256         
19257     },
19258     
19259     setValue: function(v, suppressEvent)
19260     {   
19261         var o = this.getValue();
19262         
19263         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19264         
19265         this.update();
19266
19267         if(suppressEvent !== true){
19268             this.fireEvent('select', this, o, v);
19269         }
19270         
19271     },
19272     
19273     getValue: function()
19274     {
19275         return this.value;
19276     },
19277     
19278     onClick: function(e) 
19279     {
19280         e.stopPropagation();
19281         e.preventDefault();
19282         
19283         var target = e.getTarget();
19284         
19285         if(target.nodeName.toLowerCase() === 'i'){
19286             target = Roo.get(target).dom.parentNode;
19287         }
19288         
19289         var nodeName = target.nodeName;
19290         var className = target.className;
19291         var html = target.innerHTML;
19292         
19293         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19294             return;
19295         }
19296         
19297         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19298         
19299         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19300         
19301         this.hide();
19302                         
19303     },
19304     
19305     picker : function()
19306     {
19307         return this.pickerEl;
19308     },
19309     
19310     fillMonths: function()
19311     {    
19312         var i = 0;
19313         var months = this.picker().select('>.datepicker-months td', true).first();
19314         
19315         months.dom.innerHTML = '';
19316         
19317         while (i < 12) {
19318             var month = {
19319                 tag: 'span',
19320                 cls: 'month',
19321                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19322             };
19323             
19324             months.createChild(month);
19325         }
19326         
19327     },
19328     
19329     update: function()
19330     {
19331         var _this = this;
19332         
19333         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19334             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19335         }
19336         
19337         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19338             e.removeClass('active');
19339             
19340             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19341                 e.addClass('active');
19342             }
19343         })
19344     },
19345     
19346     place: function()
19347     {
19348         if(this.isInline) {
19349             return;
19350         }
19351         
19352         this.picker().removeClass(['bottom', 'top']);
19353         
19354         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19355             /*
19356              * place to the top of element!
19357              *
19358              */
19359             
19360             this.picker().addClass('top');
19361             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19362             
19363             return;
19364         }
19365         
19366         this.picker().addClass('bottom');
19367         
19368         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19369     },
19370     
19371     onFocus : function()
19372     {
19373         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19374         this.show();
19375     },
19376     
19377     onBlur : function()
19378     {
19379         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19380         
19381         var d = this.inputEl().getValue();
19382         
19383         this.setValue(d);
19384                 
19385         this.hide();
19386     },
19387     
19388     show : function()
19389     {
19390         this.picker().show();
19391         this.picker().select('>.datepicker-months', true).first().show();
19392         this.update();
19393         this.place();
19394         
19395         this.fireEvent('show', this, this.date);
19396     },
19397     
19398     hide : function()
19399     {
19400         if(this.isInline) {
19401             return;
19402         }
19403         this.picker().hide();
19404         this.fireEvent('hide', this, this.date);
19405         
19406     },
19407     
19408     onMousedown: function(e)
19409     {
19410         e.stopPropagation();
19411         e.preventDefault();
19412     },
19413     
19414     keyup: function(e)
19415     {
19416         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19417         this.update();
19418     },
19419
19420     fireKey: function(e)
19421     {
19422         if (!this.picker().isVisible()){
19423             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19424                 this.show();
19425             }
19426             return;
19427         }
19428         
19429         var dir;
19430         
19431         switch(e.keyCode){
19432             case 27: // escape
19433                 this.hide();
19434                 e.preventDefault();
19435                 break;
19436             case 37: // left
19437             case 39: // right
19438                 dir = e.keyCode == 37 ? -1 : 1;
19439                 
19440                 this.vIndex = this.vIndex + dir;
19441                 
19442                 if(this.vIndex < 0){
19443                     this.vIndex = 0;
19444                 }
19445                 
19446                 if(this.vIndex > 11){
19447                     this.vIndex = 11;
19448                 }
19449                 
19450                 if(isNaN(this.vIndex)){
19451                     this.vIndex = 0;
19452                 }
19453                 
19454                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19455                 
19456                 break;
19457             case 38: // up
19458             case 40: // down
19459                 
19460                 dir = e.keyCode == 38 ? -1 : 1;
19461                 
19462                 this.vIndex = this.vIndex + dir * 4;
19463                 
19464                 if(this.vIndex < 0){
19465                     this.vIndex = 0;
19466                 }
19467                 
19468                 if(this.vIndex > 11){
19469                     this.vIndex = 11;
19470                 }
19471                 
19472                 if(isNaN(this.vIndex)){
19473                     this.vIndex = 0;
19474                 }
19475                 
19476                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19477                 break;
19478                 
19479             case 13: // enter
19480                 
19481                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19482                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19483                 }
19484                 
19485                 this.hide();
19486                 e.preventDefault();
19487                 break;
19488             case 9: // tab
19489                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19490                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19491                 }
19492                 this.hide();
19493                 break;
19494             case 16: // shift
19495             case 17: // ctrl
19496             case 18: // alt
19497                 break;
19498             default :
19499                 this.hide();
19500                 
19501         }
19502     },
19503     
19504     remove: function() 
19505     {
19506         this.picker().remove();
19507     }
19508    
19509 });
19510
19511 Roo.apply(Roo.bootstrap.MonthField,  {
19512     
19513     content : {
19514         tag: 'tbody',
19515         cn: [
19516         {
19517             tag: 'tr',
19518             cn: [
19519             {
19520                 tag: 'td',
19521                 colspan: '7'
19522             }
19523             ]
19524         }
19525         ]
19526     },
19527     
19528     dates:{
19529         en: {
19530             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19531             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19532         }
19533     }
19534 });
19535
19536 Roo.apply(Roo.bootstrap.MonthField,  {
19537   
19538     template : {
19539         tag: 'div',
19540         cls: 'datepicker dropdown-menu roo-dynamic',
19541         cn: [
19542             {
19543                 tag: 'div',
19544                 cls: 'datepicker-months',
19545                 cn: [
19546                 {
19547                     tag: 'table',
19548                     cls: 'table-condensed',
19549                     cn:[
19550                         Roo.bootstrap.DateField.content
19551                     ]
19552                 }
19553                 ]
19554             }
19555         ]
19556     }
19557 });
19558
19559  
19560
19561  
19562  /*
19563  * - LGPL
19564  *
19565  * CheckBox
19566  * 
19567  */
19568
19569 /**
19570  * @class Roo.bootstrap.CheckBox
19571  * @extends Roo.bootstrap.Input
19572  * Bootstrap CheckBox class
19573  * 
19574  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19575  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19576  * @cfg {String} boxLabel The text that appears beside the checkbox
19577  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19578  * @cfg {Boolean} checked initnal the element
19579  * @cfg {Boolean} inline inline the element (default false)
19580  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19581  * 
19582  * @constructor
19583  * Create a new CheckBox
19584  * @param {Object} config The config object
19585  */
19586
19587 Roo.bootstrap.CheckBox = function(config){
19588     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19589    
19590     this.addEvents({
19591         /**
19592         * @event check
19593         * Fires when the element is checked or unchecked.
19594         * @param {Roo.bootstrap.CheckBox} this This input
19595         * @param {Boolean} checked The new checked value
19596         */
19597        check : true
19598     });
19599     
19600 };
19601
19602 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19603   
19604     inputType: 'checkbox',
19605     inputValue: 1,
19606     valueOff: 0,
19607     boxLabel: false,
19608     checked: false,
19609     weight : false,
19610     inline: false,
19611     
19612     getAutoCreate : function()
19613     {
19614         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19615         
19616         var id = Roo.id();
19617         
19618         var cfg = {};
19619         
19620         cfg.cls = 'form-group ' + this.inputType; //input-group
19621         
19622         if(this.inline){
19623             cfg.cls += ' ' + this.inputType + '-inline';
19624         }
19625         
19626         var input =  {
19627             tag: 'input',
19628             id : id,
19629             type : this.inputType,
19630             value : this.inputValue,
19631             cls : 'roo-' + this.inputType, //'form-box',
19632             placeholder : this.placeholder || ''
19633             
19634         };
19635         
19636         if(this.inputType != 'radio'){
19637             var hidden =  {
19638                 tag: 'input',
19639                 type : 'hidden',
19640                 cls : 'roo-hidden-value',
19641                 value : this.checked ? this.valueOff : this.inputValue
19642             };
19643         }
19644         
19645             
19646         if (this.weight) { // Validity check?
19647             cfg.cls += " " + this.inputType + "-" + this.weight;
19648         }
19649         
19650         if (this.disabled) {
19651             input.disabled=true;
19652         }
19653         
19654         if(this.checked){
19655             input.checked = this.checked;
19656             
19657         }
19658         
19659         
19660         if (this.name) {
19661             
19662             input.name = this.name;
19663             
19664             if(this.inputType != 'radio'){
19665                 hidden.name = this.name;
19666                 input.name = '_hidden_' + this.name;
19667             }
19668         }
19669         
19670         if (this.size) {
19671             input.cls += ' input-' + this.size;
19672         }
19673         
19674         var settings=this;
19675         
19676         ['xs','sm','md','lg'].map(function(size){
19677             if (settings[size]) {
19678                 cfg.cls += ' col-' + size + '-' + settings[size];
19679             }
19680         });
19681         
19682         var inputblock = input;
19683          
19684         if (this.before || this.after) {
19685             
19686             inputblock = {
19687                 cls : 'input-group',
19688                 cn :  [] 
19689             };
19690             
19691             if (this.before) {
19692                 inputblock.cn.push({
19693                     tag :'span',
19694                     cls : 'input-group-addon',
19695                     html : this.before
19696                 });
19697             }
19698             
19699             inputblock.cn.push(input);
19700             
19701             if(this.inputType != 'radio'){
19702                 inputblock.cn.push(hidden);
19703             }
19704             
19705             if (this.after) {
19706                 inputblock.cn.push({
19707                     tag :'span',
19708                     cls : 'input-group-addon',
19709                     html : this.after
19710                 });
19711             }
19712             
19713         }
19714         
19715         if (align ==='left' && this.fieldLabel.length) {
19716 //                Roo.log("left and has label");
19717                 cfg.cn = [
19718                     
19719                     {
19720                         tag: 'label',
19721                         'for' :  id,
19722                         cls : 'control-label col-md-' + this.labelWidth,
19723                         html : this.fieldLabel
19724                         
19725                     },
19726                     {
19727                         cls : "col-md-" + (12 - this.labelWidth), 
19728                         cn: [
19729                             inputblock
19730                         ]
19731                     }
19732                     
19733                 ];
19734         } else if ( this.fieldLabel.length) {
19735 //                Roo.log(" label");
19736                 cfg.cn = [
19737                    
19738                     {
19739                         tag: this.boxLabel ? 'span' : 'label',
19740                         'for': id,
19741                         cls: 'control-label box-input-label',
19742                         //cls : 'input-group-addon',
19743                         html : this.fieldLabel
19744                         
19745                     },
19746                     
19747                     inputblock
19748                     
19749                 ];
19750
19751         } else {
19752             
19753 //                Roo.log(" no label && no align");
19754                 cfg.cn = [  inputblock ] ;
19755                 
19756                 
19757         }
19758         
19759         if(this.boxLabel){
19760              var boxLabelCfg = {
19761                 tag: 'label',
19762                 //'for': id, // box label is handled by onclick - so no for...
19763                 cls: 'box-label',
19764                 html: this.boxLabel
19765             };
19766             
19767             if(this.tooltip){
19768                 boxLabelCfg.tooltip = this.tooltip;
19769             }
19770              
19771             cfg.cn.push(boxLabelCfg);
19772         }
19773         
19774         if(this.inputType != 'radio'){
19775             cfg.cn.push(hidden);
19776         }
19777         
19778         return cfg;
19779         
19780     },
19781     
19782     /**
19783      * return the real input element.
19784      */
19785     inputEl: function ()
19786     {
19787         return this.el.select('input.roo-' + this.inputType,true).first();
19788     },
19789     hiddenEl: function ()
19790     {
19791         return this.el.select('input.roo-hidden-value',true).first();
19792     },
19793     
19794     labelEl: function()
19795     {
19796         return this.el.select('label.control-label',true).first();
19797     },
19798     /* depricated... */
19799     
19800     label: function()
19801     {
19802         return this.labelEl();
19803     },
19804     
19805     boxLabelEl: function()
19806     {
19807         return this.el.select('label.box-label',true).first();
19808     },
19809     
19810     initEvents : function()
19811     {
19812 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19813         
19814         this.inputEl().on('click', this.onClick,  this);
19815         
19816         if (this.boxLabel) { 
19817             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19818         }
19819         
19820         this.startValue = this.getValue();
19821         
19822         if(this.groupId){
19823             Roo.bootstrap.CheckBox.register(this);
19824         }
19825     },
19826     
19827     onClick : function()
19828     {   
19829         this.setChecked(!this.checked);
19830     },
19831     
19832     setChecked : function(state,suppressEvent)
19833     {
19834         this.startValue = this.getValue();
19835
19836         if(this.inputType == 'radio'){
19837             
19838             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19839                 e.dom.checked = false;
19840             });
19841             
19842             this.inputEl().dom.checked = true;
19843             
19844             this.inputEl().dom.value = this.inputValue;
19845             
19846             if(suppressEvent !== true){
19847                 this.fireEvent('check', this, true);
19848             }
19849             
19850             this.validate();
19851             
19852             return;
19853         }
19854         
19855         this.checked = state;
19856         
19857         this.inputEl().dom.checked = state;
19858         
19859         
19860         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19861         
19862         if(suppressEvent !== true){
19863             this.fireEvent('check', this, state);
19864         }
19865         
19866         this.validate();
19867     },
19868     
19869     getValue : function()
19870     {
19871         if(this.inputType == 'radio'){
19872             return this.getGroupValue();
19873         }
19874         
19875         return this.hiddenEl().dom.value;
19876         
19877     },
19878     
19879     getGroupValue : function()
19880     {
19881         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19882             return '';
19883         }
19884         
19885         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19886     },
19887     
19888     setValue : function(v,suppressEvent)
19889     {
19890         if(this.inputType == 'radio'){
19891             this.setGroupValue(v, suppressEvent);
19892             return;
19893         }
19894         
19895         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19896         
19897         this.validate();
19898     },
19899     
19900     setGroupValue : function(v, suppressEvent)
19901     {
19902         this.startValue = this.getValue();
19903         
19904         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19905             e.dom.checked = false;
19906             
19907             if(e.dom.value == v){
19908                 e.dom.checked = true;
19909             }
19910         });
19911         
19912         if(suppressEvent !== true){
19913             this.fireEvent('check', this, true);
19914         }
19915
19916         this.validate();
19917         
19918         return;
19919     },
19920     
19921     validate : function()
19922     {
19923         if(
19924                 this.disabled || 
19925                 (this.inputType == 'radio' && this.validateRadio()) ||
19926                 (this.inputType == 'checkbox' && this.validateCheckbox())
19927         ){
19928             this.markValid();
19929             return true;
19930         }
19931         
19932         this.markInvalid();
19933         return false;
19934     },
19935     
19936     validateRadio : function()
19937     {
19938         if(this.allowBlank){
19939             return true;
19940         }
19941         
19942         var valid = false;
19943         
19944         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19945             if(!e.dom.checked){
19946                 return;
19947             }
19948             
19949             valid = true;
19950             
19951             return false;
19952         });
19953         
19954         return valid;
19955     },
19956     
19957     validateCheckbox : function()
19958     {
19959         if(!this.groupId){
19960             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19961         }
19962         
19963         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19964         
19965         if(!group){
19966             return false;
19967         }
19968         
19969         var r = false;
19970         
19971         for(var i in group){
19972             if(r){
19973                 break;
19974             }
19975             
19976             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19977         }
19978         
19979         return r;
19980     },
19981     
19982     /**
19983      * Mark this field as valid
19984      */
19985     markValid : function()
19986     {
19987         var _this = this;
19988         
19989         this.fireEvent('valid', this);
19990         
19991         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19992         
19993         if(this.groupId){
19994             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19995         }
19996         
19997         if(label){
19998             label.markValid();
19999         }
20000
20001         if(this.inputType == 'radio'){
20002             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20003                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20004                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20005             });
20006             
20007             return;
20008         }
20009         
20010         if(!this.groupId){
20011             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20012             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20013             return;
20014         }
20015         
20016         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20017             
20018         if(!group){
20019             return;
20020         }
20021         
20022         for(var i in group){
20023             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20024             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20025         }
20026     },
20027     
20028      /**
20029      * Mark this field as invalid
20030      * @param {String} msg The validation message
20031      */
20032     markInvalid : function(msg)
20033     {
20034         if(this.allowBlank){
20035             return;
20036         }
20037         
20038         var _this = this;
20039         
20040         this.fireEvent('invalid', this, msg);
20041         
20042         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20043         
20044         if(this.groupId){
20045             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20046         }
20047         
20048         if(label){
20049             label.markInvalid();
20050         }
20051             
20052         if(this.inputType == 'radio'){
20053             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20054                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20055                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20056             });
20057             
20058             return;
20059         }
20060         
20061         if(!this.groupId){
20062             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20063             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20064             return;
20065         }
20066         
20067         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20068         
20069         if(!group){
20070             return;
20071         }
20072         
20073         for(var i in group){
20074             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20075             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20076         }
20077         
20078     },
20079     
20080     clearInvalid : function()
20081     {
20082         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20083         
20084         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20085         
20086         if (label) {
20087             label.iconEl.removeClass(label.validClass);
20088             label.iconEl.removeClass(label.invalidClass);
20089         }
20090     },
20091     
20092     disable : function()
20093     {
20094         if(this.inputType != 'radio'){
20095             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20096             return;
20097         }
20098         
20099         var _this = this;
20100         
20101         if(this.rendered){
20102             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20103                 _this.getActionEl().addClass(this.disabledClass);
20104                 e.dom.disabled = true;
20105             });
20106         }
20107         
20108         this.disabled = true;
20109         this.fireEvent("disable", this);
20110         return this;
20111     },
20112
20113     enable : function()
20114     {
20115         if(this.inputType != 'radio'){
20116             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20117             return;
20118         }
20119         
20120         var _this = this;
20121         
20122         if(this.rendered){
20123             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20124                 _this.getActionEl().removeClass(this.disabledClass);
20125                 e.dom.disabled = false;
20126             });
20127         }
20128         
20129         this.disabled = false;
20130         this.fireEvent("enable", this);
20131         return this;
20132     }
20133
20134 });
20135
20136 Roo.apply(Roo.bootstrap.CheckBox, {
20137     
20138     groups: {},
20139     
20140      /**
20141     * register a CheckBox Group
20142     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20143     */
20144     register : function(checkbox)
20145     {
20146         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20147             this.groups[checkbox.groupId] = {};
20148         }
20149         
20150         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20151             return;
20152         }
20153         
20154         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20155         
20156     },
20157     /**
20158     * fetch a CheckBox Group based on the group ID
20159     * @param {string} the group ID
20160     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20161     */
20162     get: function(groupId) {
20163         if (typeof(this.groups[groupId]) == 'undefined') {
20164             return false;
20165         }
20166         
20167         return this.groups[groupId] ;
20168     }
20169     
20170     
20171 });
20172 /*
20173  * - LGPL
20174  *
20175  * RadioItem
20176  * 
20177  */
20178
20179 /**
20180  * @class Roo.bootstrap.Radio
20181  * @extends Roo.bootstrap.Component
20182  * Bootstrap Radio class
20183  * @cfg {String} boxLabel - the label associated
20184  * @cfg {String} value - the value of radio
20185  * 
20186  * @constructor
20187  * Create a new Radio
20188  * @param {Object} config The config object
20189  */
20190 Roo.bootstrap.Radio = function(config){
20191     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20192     
20193 };
20194
20195 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20196     
20197     boxLabel : '',
20198     
20199     value : '',
20200     
20201     getAutoCreate : function()
20202     {
20203         var cfg = {
20204             tag : 'div',
20205             cls : 'form-group radio',
20206             cn : [
20207                 {
20208                     tag : 'label',
20209                     cls : 'box-label',
20210                     html : this.boxLabel
20211                 }
20212             ]
20213         };
20214         
20215         return cfg;
20216     },
20217     
20218     initEvents : function() 
20219     {
20220         this.parent().register(this);
20221         
20222         this.el.on('click', this.onClick, this);
20223         
20224     },
20225     
20226     onClick : function()
20227     {
20228         this.setChecked(true);
20229     },
20230     
20231     setChecked : function(state, suppressEvent)
20232     {
20233         this.parent().setValue(this.value, suppressEvent);
20234         
20235     }
20236     
20237 });
20238  
20239
20240  //<script type="text/javascript">
20241
20242 /*
20243  * Based  Ext JS Library 1.1.1
20244  * Copyright(c) 2006-2007, Ext JS, LLC.
20245  * LGPL
20246  *
20247  */
20248  
20249 /**
20250  * @class Roo.HtmlEditorCore
20251  * @extends Roo.Component
20252  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20253  *
20254  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20255  */
20256
20257 Roo.HtmlEditorCore = function(config){
20258     
20259     
20260     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20261     
20262     
20263     this.addEvents({
20264         /**
20265          * @event initialize
20266          * Fires when the editor is fully initialized (including the iframe)
20267          * @param {Roo.HtmlEditorCore} this
20268          */
20269         initialize: true,
20270         /**
20271          * @event activate
20272          * Fires when the editor is first receives the focus. Any insertion must wait
20273          * until after this event.
20274          * @param {Roo.HtmlEditorCore} this
20275          */
20276         activate: true,
20277          /**
20278          * @event beforesync
20279          * Fires before the textarea is updated with content from the editor iframe. Return false
20280          * to cancel the sync.
20281          * @param {Roo.HtmlEditorCore} this
20282          * @param {String} html
20283          */
20284         beforesync: true,
20285          /**
20286          * @event beforepush
20287          * Fires before the iframe editor is updated with content from the textarea. Return false
20288          * to cancel the push.
20289          * @param {Roo.HtmlEditorCore} this
20290          * @param {String} html
20291          */
20292         beforepush: true,
20293          /**
20294          * @event sync
20295          * Fires when the textarea is updated with content from the editor iframe.
20296          * @param {Roo.HtmlEditorCore} this
20297          * @param {String} html
20298          */
20299         sync: true,
20300          /**
20301          * @event push
20302          * Fires when the iframe editor is updated with content from the textarea.
20303          * @param {Roo.HtmlEditorCore} this
20304          * @param {String} html
20305          */
20306         push: true,
20307         
20308         /**
20309          * @event editorevent
20310          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20311          * @param {Roo.HtmlEditorCore} this
20312          */
20313         editorevent: true
20314         
20315     });
20316     
20317     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20318     
20319     // defaults : white / black...
20320     this.applyBlacklists();
20321     
20322     
20323     
20324 };
20325
20326
20327 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20328
20329
20330      /**
20331      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20332      */
20333     
20334     owner : false,
20335     
20336      /**
20337      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20338      *                        Roo.resizable.
20339      */
20340     resizable : false,
20341      /**
20342      * @cfg {Number} height (in pixels)
20343      */   
20344     height: 300,
20345    /**
20346      * @cfg {Number} width (in pixels)
20347      */   
20348     width: 500,
20349     
20350     /**
20351      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20352      * 
20353      */
20354     stylesheets: false,
20355     
20356     // id of frame..
20357     frameId: false,
20358     
20359     // private properties
20360     validationEvent : false,
20361     deferHeight: true,
20362     initialized : false,
20363     activated : false,
20364     sourceEditMode : false,
20365     onFocus : Roo.emptyFn,
20366     iframePad:3,
20367     hideMode:'offsets',
20368     
20369     clearUp: true,
20370     
20371     // blacklist + whitelisted elements..
20372     black: false,
20373     white: false,
20374      
20375     
20376
20377     /**
20378      * Protected method that will not generally be called directly. It
20379      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20380      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20381      */
20382     getDocMarkup : function(){
20383         // body styles..
20384         var st = '';
20385         
20386         // inherit styels from page...?? 
20387         if (this.stylesheets === false) {
20388             
20389             Roo.get(document.head).select('style').each(function(node) {
20390                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20391             });
20392             
20393             Roo.get(document.head).select('link').each(function(node) { 
20394                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20395             });
20396             
20397         } else if (!this.stylesheets.length) {
20398                 // simple..
20399                 st = '<style type="text/css">' +
20400                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20401                    '</style>';
20402         } else { 
20403             
20404         }
20405         
20406         st +=  '<style type="text/css">' +
20407             'IMG { cursor: pointer } ' +
20408         '</style>';
20409
20410         
20411         return '<html><head>' + st  +
20412             //<style type="text/css">' +
20413             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20414             //'</style>' +
20415             ' </head><body class="roo-htmleditor-body"></body></html>';
20416     },
20417
20418     // private
20419     onRender : function(ct, position)
20420     {
20421         var _t = this;
20422         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20423         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20424         
20425         
20426         this.el.dom.style.border = '0 none';
20427         this.el.dom.setAttribute('tabIndex', -1);
20428         this.el.addClass('x-hidden hide');
20429         
20430         
20431         
20432         if(Roo.isIE){ // fix IE 1px bogus margin
20433             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20434         }
20435        
20436         
20437         this.frameId = Roo.id();
20438         
20439          
20440         
20441         var iframe = this.owner.wrap.createChild({
20442             tag: 'iframe',
20443             cls: 'form-control', // bootstrap..
20444             id: this.frameId,
20445             name: this.frameId,
20446             frameBorder : 'no',
20447             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20448         }, this.el
20449         );
20450         
20451         
20452         this.iframe = iframe.dom;
20453
20454          this.assignDocWin();
20455         
20456         this.doc.designMode = 'on';
20457        
20458         this.doc.open();
20459         this.doc.write(this.getDocMarkup());
20460         this.doc.close();
20461
20462         
20463         var task = { // must defer to wait for browser to be ready
20464             run : function(){
20465                 //console.log("run task?" + this.doc.readyState);
20466                 this.assignDocWin();
20467                 if(this.doc.body || this.doc.readyState == 'complete'){
20468                     try {
20469                         this.doc.designMode="on";
20470                     } catch (e) {
20471                         return;
20472                     }
20473                     Roo.TaskMgr.stop(task);
20474                     this.initEditor.defer(10, this);
20475                 }
20476             },
20477             interval : 10,
20478             duration: 10000,
20479             scope: this
20480         };
20481         Roo.TaskMgr.start(task);
20482
20483     },
20484
20485     // private
20486     onResize : function(w, h)
20487     {
20488          Roo.log('resize: ' +w + ',' + h );
20489         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20490         if(!this.iframe){
20491             return;
20492         }
20493         if(typeof w == 'number'){
20494             
20495             this.iframe.style.width = w + 'px';
20496         }
20497         if(typeof h == 'number'){
20498             
20499             this.iframe.style.height = h + 'px';
20500             if(this.doc){
20501                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20502             }
20503         }
20504         
20505     },
20506
20507     /**
20508      * Toggles the editor between standard and source edit mode.
20509      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20510      */
20511     toggleSourceEdit : function(sourceEditMode){
20512         
20513         this.sourceEditMode = sourceEditMode === true;
20514         
20515         if(this.sourceEditMode){
20516  
20517             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20518             
20519         }else{
20520             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20521             //this.iframe.className = '';
20522             this.deferFocus();
20523         }
20524         //this.setSize(this.owner.wrap.getSize());
20525         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20526     },
20527
20528     
20529   
20530
20531     /**
20532      * Protected method that will not generally be called directly. If you need/want
20533      * custom HTML cleanup, this is the method you should override.
20534      * @param {String} html The HTML to be cleaned
20535      * return {String} The cleaned HTML
20536      */
20537     cleanHtml : function(html){
20538         html = String(html);
20539         if(html.length > 5){
20540             if(Roo.isSafari){ // strip safari nonsense
20541                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20542             }
20543         }
20544         if(html == '&nbsp;'){
20545             html = '';
20546         }
20547         return html;
20548     },
20549
20550     /**
20551      * HTML Editor -> Textarea
20552      * Protected method that will not generally be called directly. Syncs the contents
20553      * of the editor iframe with the textarea.
20554      */
20555     syncValue : function(){
20556         if(this.initialized){
20557             var bd = (this.doc.body || this.doc.documentElement);
20558             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20559             var html = bd.innerHTML;
20560             if(Roo.isSafari){
20561                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20562                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20563                 if(m && m[1]){
20564                     html = '<div style="'+m[0]+'">' + html + '</div>';
20565                 }
20566             }
20567             html = this.cleanHtml(html);
20568             // fix up the special chars.. normaly like back quotes in word...
20569             // however we do not want to do this with chinese..
20570             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20571                 var cc = b.charCodeAt();
20572                 if (
20573                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20574                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20575                     (cc >= 0xf900 && cc < 0xfb00 )
20576                 ) {
20577                         return b;
20578                 }
20579                 return "&#"+cc+";" 
20580             });
20581             if(this.owner.fireEvent('beforesync', this, html) !== false){
20582                 this.el.dom.value = html;
20583                 this.owner.fireEvent('sync', this, html);
20584             }
20585         }
20586     },
20587
20588     /**
20589      * Protected method that will not generally be called directly. Pushes the value of the textarea
20590      * into the iframe editor.
20591      */
20592     pushValue : function(){
20593         if(this.initialized){
20594             var v = this.el.dom.value.trim();
20595             
20596 //            if(v.length < 1){
20597 //                v = '&#160;';
20598 //            }
20599             
20600             if(this.owner.fireEvent('beforepush', this, v) !== false){
20601                 var d = (this.doc.body || this.doc.documentElement);
20602                 d.innerHTML = v;
20603                 this.cleanUpPaste();
20604                 this.el.dom.value = d.innerHTML;
20605                 this.owner.fireEvent('push', this, v);
20606             }
20607         }
20608     },
20609
20610     // private
20611     deferFocus : function(){
20612         this.focus.defer(10, this);
20613     },
20614
20615     // doc'ed in Field
20616     focus : function(){
20617         if(this.win && !this.sourceEditMode){
20618             this.win.focus();
20619         }else{
20620             this.el.focus();
20621         }
20622     },
20623     
20624     assignDocWin: function()
20625     {
20626         var iframe = this.iframe;
20627         
20628          if(Roo.isIE){
20629             this.doc = iframe.contentWindow.document;
20630             this.win = iframe.contentWindow;
20631         } else {
20632 //            if (!Roo.get(this.frameId)) {
20633 //                return;
20634 //            }
20635 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20636 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20637             
20638             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20639                 return;
20640             }
20641             
20642             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20643             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20644         }
20645     },
20646     
20647     // private
20648     initEditor : function(){
20649         //console.log("INIT EDITOR");
20650         this.assignDocWin();
20651         
20652         
20653         
20654         this.doc.designMode="on";
20655         this.doc.open();
20656         this.doc.write(this.getDocMarkup());
20657         this.doc.close();
20658         
20659         var dbody = (this.doc.body || this.doc.documentElement);
20660         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20661         // this copies styles from the containing element into thsi one..
20662         // not sure why we need all of this..
20663         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20664         
20665         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20666         //ss['background-attachment'] = 'fixed'; // w3c
20667         dbody.bgProperties = 'fixed'; // ie
20668         //Roo.DomHelper.applyStyles(dbody, ss);
20669         Roo.EventManager.on(this.doc, {
20670             //'mousedown': this.onEditorEvent,
20671             'mouseup': this.onEditorEvent,
20672             'dblclick': this.onEditorEvent,
20673             'click': this.onEditorEvent,
20674             'keyup': this.onEditorEvent,
20675             buffer:100,
20676             scope: this
20677         });
20678         if(Roo.isGecko){
20679             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20680         }
20681         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20682             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20683         }
20684         this.initialized = true;
20685
20686         this.owner.fireEvent('initialize', this);
20687         this.pushValue();
20688     },
20689
20690     // private
20691     onDestroy : function(){
20692         
20693         
20694         
20695         if(this.rendered){
20696             
20697             //for (var i =0; i < this.toolbars.length;i++) {
20698             //    // fixme - ask toolbars for heights?
20699             //    this.toolbars[i].onDestroy();
20700            // }
20701             
20702             //this.wrap.dom.innerHTML = '';
20703             //this.wrap.remove();
20704         }
20705     },
20706
20707     // private
20708     onFirstFocus : function(){
20709         
20710         this.assignDocWin();
20711         
20712         
20713         this.activated = true;
20714          
20715     
20716         if(Roo.isGecko){ // prevent silly gecko errors
20717             this.win.focus();
20718             var s = this.win.getSelection();
20719             if(!s.focusNode || s.focusNode.nodeType != 3){
20720                 var r = s.getRangeAt(0);
20721                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20722                 r.collapse(true);
20723                 this.deferFocus();
20724             }
20725             try{
20726                 this.execCmd('useCSS', true);
20727                 this.execCmd('styleWithCSS', false);
20728             }catch(e){}
20729         }
20730         this.owner.fireEvent('activate', this);
20731     },
20732
20733     // private
20734     adjustFont: function(btn){
20735         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20736         //if(Roo.isSafari){ // safari
20737         //    adjust *= 2;
20738        // }
20739         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20740         if(Roo.isSafari){ // safari
20741             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20742             v =  (v < 10) ? 10 : v;
20743             v =  (v > 48) ? 48 : v;
20744             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20745             
20746         }
20747         
20748         
20749         v = Math.max(1, v+adjust);
20750         
20751         this.execCmd('FontSize', v  );
20752     },
20753
20754     onEditorEvent : function(e)
20755     {
20756         this.owner.fireEvent('editorevent', this, e);
20757       //  this.updateToolbar();
20758         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20759     },
20760
20761     insertTag : function(tg)
20762     {
20763         // could be a bit smarter... -> wrap the current selected tRoo..
20764         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20765             
20766             range = this.createRange(this.getSelection());
20767             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20768             wrappingNode.appendChild(range.extractContents());
20769             range.insertNode(wrappingNode);
20770
20771             return;
20772             
20773             
20774             
20775         }
20776         this.execCmd("formatblock",   tg);
20777         
20778     },
20779     
20780     insertText : function(txt)
20781     {
20782         
20783         
20784         var range = this.createRange();
20785         range.deleteContents();
20786                //alert(Sender.getAttribute('label'));
20787                
20788         range.insertNode(this.doc.createTextNode(txt));
20789     } ,
20790     
20791      
20792
20793     /**
20794      * Executes a Midas editor command on the editor document and performs necessary focus and
20795      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20796      * @param {String} cmd The Midas command
20797      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20798      */
20799     relayCmd : function(cmd, value){
20800         this.win.focus();
20801         this.execCmd(cmd, value);
20802         this.owner.fireEvent('editorevent', this);
20803         //this.updateToolbar();
20804         this.owner.deferFocus();
20805     },
20806
20807     /**
20808      * Executes a Midas editor command directly on the editor document.
20809      * For visual commands, you should use {@link #relayCmd} instead.
20810      * <b>This should only be called after the editor is initialized.</b>
20811      * @param {String} cmd The Midas command
20812      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20813      */
20814     execCmd : function(cmd, value){
20815         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20816         this.syncValue();
20817     },
20818  
20819  
20820    
20821     /**
20822      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20823      * to insert tRoo.
20824      * @param {String} text | dom node.. 
20825      */
20826     insertAtCursor : function(text)
20827     {
20828         
20829         
20830         
20831         if(!this.activated){
20832             return;
20833         }
20834         /*
20835         if(Roo.isIE){
20836             this.win.focus();
20837             var r = this.doc.selection.createRange();
20838             if(r){
20839                 r.collapse(true);
20840                 r.pasteHTML(text);
20841                 this.syncValue();
20842                 this.deferFocus();
20843             
20844             }
20845             return;
20846         }
20847         */
20848         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20849             this.win.focus();
20850             
20851             
20852             // from jquery ui (MIT licenced)
20853             var range, node;
20854             var win = this.win;
20855             
20856             if (win.getSelection && win.getSelection().getRangeAt) {
20857                 range = win.getSelection().getRangeAt(0);
20858                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20859                 range.insertNode(node);
20860             } else if (win.document.selection && win.document.selection.createRange) {
20861                 // no firefox support
20862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20863                 win.document.selection.createRange().pasteHTML(txt);
20864             } else {
20865                 // no firefox support
20866                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20867                 this.execCmd('InsertHTML', txt);
20868             } 
20869             
20870             this.syncValue();
20871             
20872             this.deferFocus();
20873         }
20874     },
20875  // private
20876     mozKeyPress : function(e){
20877         if(e.ctrlKey){
20878             var c = e.getCharCode(), cmd;
20879           
20880             if(c > 0){
20881                 c = String.fromCharCode(c).toLowerCase();
20882                 switch(c){
20883                     case 'b':
20884                         cmd = 'bold';
20885                         break;
20886                     case 'i':
20887                         cmd = 'italic';
20888                         break;
20889                     
20890                     case 'u':
20891                         cmd = 'underline';
20892                         break;
20893                     
20894                     case 'v':
20895                         this.cleanUpPaste.defer(100, this);
20896                         return;
20897                         
20898                 }
20899                 if(cmd){
20900                     this.win.focus();
20901                     this.execCmd(cmd);
20902                     this.deferFocus();
20903                     e.preventDefault();
20904                 }
20905                 
20906             }
20907         }
20908     },
20909
20910     // private
20911     fixKeys : function(){ // load time branching for fastest keydown performance
20912         if(Roo.isIE){
20913             return function(e){
20914                 var k = e.getKey(), r;
20915                 if(k == e.TAB){
20916                     e.stopEvent();
20917                     r = this.doc.selection.createRange();
20918                     if(r){
20919                         r.collapse(true);
20920                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20921                         this.deferFocus();
20922                     }
20923                     return;
20924                 }
20925                 
20926                 if(k == e.ENTER){
20927                     r = this.doc.selection.createRange();
20928                     if(r){
20929                         var target = r.parentElement();
20930                         if(!target || target.tagName.toLowerCase() != 'li'){
20931                             e.stopEvent();
20932                             r.pasteHTML('<br />');
20933                             r.collapse(false);
20934                             r.select();
20935                         }
20936                     }
20937                 }
20938                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20939                     this.cleanUpPaste.defer(100, this);
20940                     return;
20941                 }
20942                 
20943                 
20944             };
20945         }else if(Roo.isOpera){
20946             return function(e){
20947                 var k = e.getKey();
20948                 if(k == e.TAB){
20949                     e.stopEvent();
20950                     this.win.focus();
20951                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20952                     this.deferFocus();
20953                 }
20954                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20955                     this.cleanUpPaste.defer(100, this);
20956                     return;
20957                 }
20958                 
20959             };
20960         }else if(Roo.isSafari){
20961             return function(e){
20962                 var k = e.getKey();
20963                 
20964                 if(k == e.TAB){
20965                     e.stopEvent();
20966                     this.execCmd('InsertText','\t');
20967                     this.deferFocus();
20968                     return;
20969                 }
20970                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20971                     this.cleanUpPaste.defer(100, this);
20972                     return;
20973                 }
20974                 
20975              };
20976         }
20977     }(),
20978     
20979     getAllAncestors: function()
20980     {
20981         var p = this.getSelectedNode();
20982         var a = [];
20983         if (!p) {
20984             a.push(p); // push blank onto stack..
20985             p = this.getParentElement();
20986         }
20987         
20988         
20989         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20990             a.push(p);
20991             p = p.parentNode;
20992         }
20993         a.push(this.doc.body);
20994         return a;
20995     },
20996     lastSel : false,
20997     lastSelNode : false,
20998     
20999     
21000     getSelection : function() 
21001     {
21002         this.assignDocWin();
21003         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21004     },
21005     
21006     getSelectedNode: function() 
21007     {
21008         // this may only work on Gecko!!!
21009         
21010         // should we cache this!!!!
21011         
21012         
21013         
21014          
21015         var range = this.createRange(this.getSelection()).cloneRange();
21016         
21017         if (Roo.isIE) {
21018             var parent = range.parentElement();
21019             while (true) {
21020                 var testRange = range.duplicate();
21021                 testRange.moveToElementText(parent);
21022                 if (testRange.inRange(range)) {
21023                     break;
21024                 }
21025                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21026                     break;
21027                 }
21028                 parent = parent.parentElement;
21029             }
21030             return parent;
21031         }
21032         
21033         // is ancestor a text element.
21034         var ac =  range.commonAncestorContainer;
21035         if (ac.nodeType == 3) {
21036             ac = ac.parentNode;
21037         }
21038         
21039         var ar = ac.childNodes;
21040          
21041         var nodes = [];
21042         var other_nodes = [];
21043         var has_other_nodes = false;
21044         for (var i=0;i<ar.length;i++) {
21045             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21046                 continue;
21047             }
21048             // fullly contained node.
21049             
21050             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21051                 nodes.push(ar[i]);
21052                 continue;
21053             }
21054             
21055             // probably selected..
21056             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21057                 other_nodes.push(ar[i]);
21058                 continue;
21059             }
21060             // outer..
21061             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21062                 continue;
21063             }
21064             
21065             
21066             has_other_nodes = true;
21067         }
21068         if (!nodes.length && other_nodes.length) {
21069             nodes= other_nodes;
21070         }
21071         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21072             return false;
21073         }
21074         
21075         return nodes[0];
21076     },
21077     createRange: function(sel)
21078     {
21079         // this has strange effects when using with 
21080         // top toolbar - not sure if it's a great idea.
21081         //this.editor.contentWindow.focus();
21082         if (typeof sel != "undefined") {
21083             try {
21084                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21085             } catch(e) {
21086                 return this.doc.createRange();
21087             }
21088         } else {
21089             return this.doc.createRange();
21090         }
21091     },
21092     getParentElement: function()
21093     {
21094         
21095         this.assignDocWin();
21096         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21097         
21098         var range = this.createRange(sel);
21099          
21100         try {
21101             var p = range.commonAncestorContainer;
21102             while (p.nodeType == 3) { // text node
21103                 p = p.parentNode;
21104             }
21105             return p;
21106         } catch (e) {
21107             return null;
21108         }
21109     
21110     },
21111     /***
21112      *
21113      * Range intersection.. the hard stuff...
21114      *  '-1' = before
21115      *  '0' = hits..
21116      *  '1' = after.
21117      *         [ -- selected range --- ]
21118      *   [fail]                        [fail]
21119      *
21120      *    basically..
21121      *      if end is before start or  hits it. fail.
21122      *      if start is after end or hits it fail.
21123      *
21124      *   if either hits (but other is outside. - then it's not 
21125      *   
21126      *    
21127      **/
21128     
21129     
21130     // @see http://www.thismuchiknow.co.uk/?p=64.
21131     rangeIntersectsNode : function(range, node)
21132     {
21133         var nodeRange = node.ownerDocument.createRange();
21134         try {
21135             nodeRange.selectNode(node);
21136         } catch (e) {
21137             nodeRange.selectNodeContents(node);
21138         }
21139     
21140         var rangeStartRange = range.cloneRange();
21141         rangeStartRange.collapse(true);
21142     
21143         var rangeEndRange = range.cloneRange();
21144         rangeEndRange.collapse(false);
21145     
21146         var nodeStartRange = nodeRange.cloneRange();
21147         nodeStartRange.collapse(true);
21148     
21149         var nodeEndRange = nodeRange.cloneRange();
21150         nodeEndRange.collapse(false);
21151     
21152         return rangeStartRange.compareBoundaryPoints(
21153                  Range.START_TO_START, nodeEndRange) == -1 &&
21154                rangeEndRange.compareBoundaryPoints(
21155                  Range.START_TO_START, nodeStartRange) == 1;
21156         
21157          
21158     },
21159     rangeCompareNode : function(range, node)
21160     {
21161         var nodeRange = node.ownerDocument.createRange();
21162         try {
21163             nodeRange.selectNode(node);
21164         } catch (e) {
21165             nodeRange.selectNodeContents(node);
21166         }
21167         
21168         
21169         range.collapse(true);
21170     
21171         nodeRange.collapse(true);
21172      
21173         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21174         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21175          
21176         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21177         
21178         var nodeIsBefore   =  ss == 1;
21179         var nodeIsAfter    = ee == -1;
21180         
21181         if (nodeIsBefore && nodeIsAfter) {
21182             return 0; // outer
21183         }
21184         if (!nodeIsBefore && nodeIsAfter) {
21185             return 1; //right trailed.
21186         }
21187         
21188         if (nodeIsBefore && !nodeIsAfter) {
21189             return 2;  // left trailed.
21190         }
21191         // fully contined.
21192         return 3;
21193     },
21194
21195     // private? - in a new class?
21196     cleanUpPaste :  function()
21197     {
21198         // cleans up the whole document..
21199         Roo.log('cleanuppaste');
21200         
21201         this.cleanUpChildren(this.doc.body);
21202         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21203         if (clean != this.doc.body.innerHTML) {
21204             this.doc.body.innerHTML = clean;
21205         }
21206         
21207     },
21208     
21209     cleanWordChars : function(input) {// change the chars to hex code
21210         var he = Roo.HtmlEditorCore;
21211         
21212         var output = input;
21213         Roo.each(he.swapCodes, function(sw) { 
21214             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21215             
21216             output = output.replace(swapper, sw[1]);
21217         });
21218         
21219         return output;
21220     },
21221     
21222     
21223     cleanUpChildren : function (n)
21224     {
21225         if (!n.childNodes.length) {
21226             return;
21227         }
21228         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21229            this.cleanUpChild(n.childNodes[i]);
21230         }
21231     },
21232     
21233     
21234         
21235     
21236     cleanUpChild : function (node)
21237     {
21238         var ed = this;
21239         //console.log(node);
21240         if (node.nodeName == "#text") {
21241             // clean up silly Windows -- stuff?
21242             return; 
21243         }
21244         if (node.nodeName == "#comment") {
21245             node.parentNode.removeChild(node);
21246             // clean up silly Windows -- stuff?
21247             return; 
21248         }
21249         var lcname = node.tagName.toLowerCase();
21250         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21251         // whitelist of tags..
21252         
21253         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21254             // remove node.
21255             node.parentNode.removeChild(node);
21256             return;
21257             
21258         }
21259         
21260         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21261         
21262         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21263         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21264         
21265         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21266         //    remove_keep_children = true;
21267         //}
21268         
21269         if (remove_keep_children) {
21270             this.cleanUpChildren(node);
21271             // inserts everything just before this node...
21272             while (node.childNodes.length) {
21273                 var cn = node.childNodes[0];
21274                 node.removeChild(cn);
21275                 node.parentNode.insertBefore(cn, node);
21276             }
21277             node.parentNode.removeChild(node);
21278             return;
21279         }
21280         
21281         if (!node.attributes || !node.attributes.length) {
21282             this.cleanUpChildren(node);
21283             return;
21284         }
21285         
21286         function cleanAttr(n,v)
21287         {
21288             
21289             if (v.match(/^\./) || v.match(/^\//)) {
21290                 return;
21291             }
21292             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21293                 return;
21294             }
21295             if (v.match(/^#/)) {
21296                 return;
21297             }
21298 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21299             node.removeAttribute(n);
21300             
21301         }
21302         
21303         var cwhite = this.cwhite;
21304         var cblack = this.cblack;
21305             
21306         function cleanStyle(n,v)
21307         {
21308             if (v.match(/expression/)) { //XSS?? should we even bother..
21309                 node.removeAttribute(n);
21310                 return;
21311             }
21312             
21313             var parts = v.split(/;/);
21314             var clean = [];
21315             
21316             Roo.each(parts, function(p) {
21317                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21318                 if (!p.length) {
21319                     return true;
21320                 }
21321                 var l = p.split(':').shift().replace(/\s+/g,'');
21322                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21323                 
21324                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21325 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21326                     //node.removeAttribute(n);
21327                     return true;
21328                 }
21329                 //Roo.log()
21330                 // only allow 'c whitelisted system attributes'
21331                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21332 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21333                     //node.removeAttribute(n);
21334                     return true;
21335                 }
21336                 
21337                 
21338                  
21339                 
21340                 clean.push(p);
21341                 return true;
21342             });
21343             if (clean.length) { 
21344                 node.setAttribute(n, clean.join(';'));
21345             } else {
21346                 node.removeAttribute(n);
21347             }
21348             
21349         }
21350         
21351         
21352         for (var i = node.attributes.length-1; i > -1 ; i--) {
21353             var a = node.attributes[i];
21354             //console.log(a);
21355             
21356             if (a.name.toLowerCase().substr(0,2)=='on')  {
21357                 node.removeAttribute(a.name);
21358                 continue;
21359             }
21360             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21361                 node.removeAttribute(a.name);
21362                 continue;
21363             }
21364             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21365                 cleanAttr(a.name,a.value); // fixme..
21366                 continue;
21367             }
21368             if (a.name == 'style') {
21369                 cleanStyle(a.name,a.value);
21370                 continue;
21371             }
21372             /// clean up MS crap..
21373             // tecnically this should be a list of valid class'es..
21374             
21375             
21376             if (a.name == 'class') {
21377                 if (a.value.match(/^Mso/)) {
21378                     node.className = '';
21379                 }
21380                 
21381                 if (a.value.match(/body/)) {
21382                     node.className = '';
21383                 }
21384                 continue;
21385             }
21386             
21387             // style cleanup!?
21388             // class cleanup?
21389             
21390         }
21391         
21392         
21393         this.cleanUpChildren(node);
21394         
21395         
21396     },
21397     
21398     /**
21399      * Clean up MS wordisms...
21400      */
21401     cleanWord : function(node)
21402     {
21403         
21404         
21405         if (!node) {
21406             this.cleanWord(this.doc.body);
21407             return;
21408         }
21409         if (node.nodeName == "#text") {
21410             // clean up silly Windows -- stuff?
21411             return; 
21412         }
21413         if (node.nodeName == "#comment") {
21414             node.parentNode.removeChild(node);
21415             // clean up silly Windows -- stuff?
21416             return; 
21417         }
21418         
21419         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21420             node.parentNode.removeChild(node);
21421             return;
21422         }
21423         
21424         // remove - but keep children..
21425         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21426             while (node.childNodes.length) {
21427                 var cn = node.childNodes[0];
21428                 node.removeChild(cn);
21429                 node.parentNode.insertBefore(cn, node);
21430             }
21431             node.parentNode.removeChild(node);
21432             this.iterateChildren(node, this.cleanWord);
21433             return;
21434         }
21435         // clean styles
21436         if (node.className.length) {
21437             
21438             var cn = node.className.split(/\W+/);
21439             var cna = [];
21440             Roo.each(cn, function(cls) {
21441                 if (cls.match(/Mso[a-zA-Z]+/)) {
21442                     return;
21443                 }
21444                 cna.push(cls);
21445             });
21446             node.className = cna.length ? cna.join(' ') : '';
21447             if (!cna.length) {
21448                 node.removeAttribute("class");
21449             }
21450         }
21451         
21452         if (node.hasAttribute("lang")) {
21453             node.removeAttribute("lang");
21454         }
21455         
21456         if (node.hasAttribute("style")) {
21457             
21458             var styles = node.getAttribute("style").split(";");
21459             var nstyle = [];
21460             Roo.each(styles, function(s) {
21461                 if (!s.match(/:/)) {
21462                     return;
21463                 }
21464                 var kv = s.split(":");
21465                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21466                     return;
21467                 }
21468                 // what ever is left... we allow.
21469                 nstyle.push(s);
21470             });
21471             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21472             if (!nstyle.length) {
21473                 node.removeAttribute('style');
21474             }
21475         }
21476         this.iterateChildren(node, this.cleanWord);
21477         
21478         
21479         
21480     },
21481     /**
21482      * iterateChildren of a Node, calling fn each time, using this as the scole..
21483      * @param {DomNode} node node to iterate children of.
21484      * @param {Function} fn method of this class to call on each item.
21485      */
21486     iterateChildren : function(node, fn)
21487     {
21488         if (!node.childNodes.length) {
21489                 return;
21490         }
21491         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21492            fn.call(this, node.childNodes[i])
21493         }
21494     },
21495     
21496     
21497     /**
21498      * cleanTableWidths.
21499      *
21500      * Quite often pasting from word etc.. results in tables with column and widths.
21501      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21502      *
21503      */
21504     cleanTableWidths : function(node)
21505     {
21506          
21507          
21508         if (!node) {
21509             this.cleanTableWidths(this.doc.body);
21510             return;
21511         }
21512         
21513         // ignore list...
21514         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21515             return; 
21516         }
21517         Roo.log(node.tagName);
21518         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21519             this.iterateChildren(node, this.cleanTableWidths);
21520             return;
21521         }
21522         if (node.hasAttribute('width')) {
21523             node.removeAttribute('width');
21524         }
21525         
21526          
21527         if (node.hasAttribute("style")) {
21528             // pretty basic...
21529             
21530             var styles = node.getAttribute("style").split(";");
21531             var nstyle = [];
21532             Roo.each(styles, function(s) {
21533                 if (!s.match(/:/)) {
21534                     return;
21535                 }
21536                 var kv = s.split(":");
21537                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21538                     return;
21539                 }
21540                 // what ever is left... we allow.
21541                 nstyle.push(s);
21542             });
21543             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21544             if (!nstyle.length) {
21545                 node.removeAttribute('style');
21546             }
21547         }
21548         
21549         this.iterateChildren(node, this.cleanTableWidths);
21550         
21551         
21552     },
21553     
21554     
21555     
21556     
21557     domToHTML : function(currentElement, depth, nopadtext) {
21558         
21559         depth = depth || 0;
21560         nopadtext = nopadtext || false;
21561     
21562         if (!currentElement) {
21563             return this.domToHTML(this.doc.body);
21564         }
21565         
21566         //Roo.log(currentElement);
21567         var j;
21568         var allText = false;
21569         var nodeName = currentElement.nodeName;
21570         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21571         
21572         if  (nodeName == '#text') {
21573             
21574             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21575         }
21576         
21577         
21578         var ret = '';
21579         if (nodeName != 'BODY') {
21580              
21581             var i = 0;
21582             // Prints the node tagName, such as <A>, <IMG>, etc
21583             if (tagName) {
21584                 var attr = [];
21585                 for(i = 0; i < currentElement.attributes.length;i++) {
21586                     // quoting?
21587                     var aname = currentElement.attributes.item(i).name;
21588                     if (!currentElement.attributes.item(i).value.length) {
21589                         continue;
21590                     }
21591                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21592                 }
21593                 
21594                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21595             } 
21596             else {
21597                 
21598                 // eack
21599             }
21600         } else {
21601             tagName = false;
21602         }
21603         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21604             return ret;
21605         }
21606         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21607             nopadtext = true;
21608         }
21609         
21610         
21611         // Traverse the tree
21612         i = 0;
21613         var currentElementChild = currentElement.childNodes.item(i);
21614         var allText = true;
21615         var innerHTML  = '';
21616         lastnode = '';
21617         while (currentElementChild) {
21618             // Formatting code (indent the tree so it looks nice on the screen)
21619             var nopad = nopadtext;
21620             if (lastnode == 'SPAN') {
21621                 nopad  = true;
21622             }
21623             // text
21624             if  (currentElementChild.nodeName == '#text') {
21625                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21626                 toadd = nopadtext ? toadd : toadd.trim();
21627                 if (!nopad && toadd.length > 80) {
21628                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21629                 }
21630                 innerHTML  += toadd;
21631                 
21632                 i++;
21633                 currentElementChild = currentElement.childNodes.item(i);
21634                 lastNode = '';
21635                 continue;
21636             }
21637             allText = false;
21638             
21639             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21640                 
21641             // Recursively traverse the tree structure of the child node
21642             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21643             lastnode = currentElementChild.nodeName;
21644             i++;
21645             currentElementChild=currentElement.childNodes.item(i);
21646         }
21647         
21648         ret += innerHTML;
21649         
21650         if (!allText) {
21651                 // The remaining code is mostly for formatting the tree
21652             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21653         }
21654         
21655         
21656         if (tagName) {
21657             ret+= "</"+tagName+">";
21658         }
21659         return ret;
21660         
21661     },
21662         
21663     applyBlacklists : function()
21664     {
21665         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21666         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21667         
21668         this.white = [];
21669         this.black = [];
21670         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21671             if (b.indexOf(tag) > -1) {
21672                 return;
21673             }
21674             this.white.push(tag);
21675             
21676         }, this);
21677         
21678         Roo.each(w, function(tag) {
21679             if (b.indexOf(tag) > -1) {
21680                 return;
21681             }
21682             if (this.white.indexOf(tag) > -1) {
21683                 return;
21684             }
21685             this.white.push(tag);
21686             
21687         }, this);
21688         
21689         
21690         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21691             if (w.indexOf(tag) > -1) {
21692                 return;
21693             }
21694             this.black.push(tag);
21695             
21696         }, this);
21697         
21698         Roo.each(b, function(tag) {
21699             if (w.indexOf(tag) > -1) {
21700                 return;
21701             }
21702             if (this.black.indexOf(tag) > -1) {
21703                 return;
21704             }
21705             this.black.push(tag);
21706             
21707         }, this);
21708         
21709         
21710         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21711         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21712         
21713         this.cwhite = [];
21714         this.cblack = [];
21715         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21716             if (b.indexOf(tag) > -1) {
21717                 return;
21718             }
21719             this.cwhite.push(tag);
21720             
21721         }, this);
21722         
21723         Roo.each(w, function(tag) {
21724             if (b.indexOf(tag) > -1) {
21725                 return;
21726             }
21727             if (this.cwhite.indexOf(tag) > -1) {
21728                 return;
21729             }
21730             this.cwhite.push(tag);
21731             
21732         }, this);
21733         
21734         
21735         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21736             if (w.indexOf(tag) > -1) {
21737                 return;
21738             }
21739             this.cblack.push(tag);
21740             
21741         }, this);
21742         
21743         Roo.each(b, function(tag) {
21744             if (w.indexOf(tag) > -1) {
21745                 return;
21746             }
21747             if (this.cblack.indexOf(tag) > -1) {
21748                 return;
21749             }
21750             this.cblack.push(tag);
21751             
21752         }, this);
21753     },
21754     
21755     setStylesheets : function(stylesheets)
21756     {
21757         if(typeof(stylesheets) == 'string'){
21758             Roo.get(this.iframe.contentDocument.head).createChild({
21759                 tag : 'link',
21760                 rel : 'stylesheet',
21761                 type : 'text/css',
21762                 href : stylesheets
21763             });
21764             
21765             return;
21766         }
21767         var _this = this;
21768      
21769         Roo.each(stylesheets, function(s) {
21770             if(!s.length){
21771                 return;
21772             }
21773             
21774             Roo.get(_this.iframe.contentDocument.head).createChild({
21775                 tag : 'link',
21776                 rel : 'stylesheet',
21777                 type : 'text/css',
21778                 href : s
21779             });
21780         });
21781
21782         
21783     },
21784     
21785     removeStylesheets : function()
21786     {
21787         var _this = this;
21788         
21789         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21790             s.remove();
21791         });
21792     }
21793     
21794     // hide stuff that is not compatible
21795     /**
21796      * @event blur
21797      * @hide
21798      */
21799     /**
21800      * @event change
21801      * @hide
21802      */
21803     /**
21804      * @event focus
21805      * @hide
21806      */
21807     /**
21808      * @event specialkey
21809      * @hide
21810      */
21811     /**
21812      * @cfg {String} fieldClass @hide
21813      */
21814     /**
21815      * @cfg {String} focusClass @hide
21816      */
21817     /**
21818      * @cfg {String} autoCreate @hide
21819      */
21820     /**
21821      * @cfg {String} inputType @hide
21822      */
21823     /**
21824      * @cfg {String} invalidClass @hide
21825      */
21826     /**
21827      * @cfg {String} invalidText @hide
21828      */
21829     /**
21830      * @cfg {String} msgFx @hide
21831      */
21832     /**
21833      * @cfg {String} validateOnBlur @hide
21834      */
21835 });
21836
21837 Roo.HtmlEditorCore.white = [
21838         'area', 'br', 'img', 'input', 'hr', 'wbr',
21839         
21840        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21841        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21842        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21843        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21844        'table',   'ul',         'xmp', 
21845        
21846        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21847       'thead',   'tr', 
21848      
21849       'dir', 'menu', 'ol', 'ul', 'dl',
21850        
21851       'embed',  'object'
21852 ];
21853
21854
21855 Roo.HtmlEditorCore.black = [
21856     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21857         'applet', // 
21858         'base',   'basefont', 'bgsound', 'blink',  'body', 
21859         'frame',  'frameset', 'head',    'html',   'ilayer', 
21860         'iframe', 'layer',  'link',     'meta',    'object',   
21861         'script', 'style' ,'title',  'xml' // clean later..
21862 ];
21863 Roo.HtmlEditorCore.clean = [
21864     'script', 'style', 'title', 'xml'
21865 ];
21866 Roo.HtmlEditorCore.remove = [
21867     'font'
21868 ];
21869 // attributes..
21870
21871 Roo.HtmlEditorCore.ablack = [
21872     'on'
21873 ];
21874     
21875 Roo.HtmlEditorCore.aclean = [ 
21876     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21877 ];
21878
21879 // protocols..
21880 Roo.HtmlEditorCore.pwhite= [
21881         'http',  'https',  'mailto'
21882 ];
21883
21884 // white listed style attributes.
21885 Roo.HtmlEditorCore.cwhite= [
21886       //  'text-align', /// default is to allow most things..
21887       
21888          
21889 //        'font-size'//??
21890 ];
21891
21892 // black listed style attributes.
21893 Roo.HtmlEditorCore.cblack= [
21894       //  'font-size' -- this can be set by the project 
21895 ];
21896
21897
21898 Roo.HtmlEditorCore.swapCodes   =[ 
21899     [    8211, "--" ], 
21900     [    8212, "--" ], 
21901     [    8216,  "'" ],  
21902     [    8217, "'" ],  
21903     [    8220, '"' ],  
21904     [    8221, '"' ],  
21905     [    8226, "*" ],  
21906     [    8230, "..." ]
21907 ]; 
21908
21909     /*
21910  * - LGPL
21911  *
21912  * HtmlEditor
21913  * 
21914  */
21915
21916 /**
21917  * @class Roo.bootstrap.HtmlEditor
21918  * @extends Roo.bootstrap.TextArea
21919  * Bootstrap HtmlEditor class
21920
21921  * @constructor
21922  * Create a new HtmlEditor
21923  * @param {Object} config The config object
21924  */
21925
21926 Roo.bootstrap.HtmlEditor = function(config){
21927     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21928     if (!this.toolbars) {
21929         this.toolbars = [];
21930     }
21931     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21932     this.addEvents({
21933             /**
21934              * @event initialize
21935              * Fires when the editor is fully initialized (including the iframe)
21936              * @param {HtmlEditor} this
21937              */
21938             initialize: true,
21939             /**
21940              * @event activate
21941              * Fires when the editor is first receives the focus. Any insertion must wait
21942              * until after this event.
21943              * @param {HtmlEditor} this
21944              */
21945             activate: true,
21946              /**
21947              * @event beforesync
21948              * Fires before the textarea is updated with content from the editor iframe. Return false
21949              * to cancel the sync.
21950              * @param {HtmlEditor} this
21951              * @param {String} html
21952              */
21953             beforesync: true,
21954              /**
21955              * @event beforepush
21956              * Fires before the iframe editor is updated with content from the textarea. Return false
21957              * to cancel the push.
21958              * @param {HtmlEditor} this
21959              * @param {String} html
21960              */
21961             beforepush: true,
21962              /**
21963              * @event sync
21964              * Fires when the textarea is updated with content from the editor iframe.
21965              * @param {HtmlEditor} this
21966              * @param {String} html
21967              */
21968             sync: true,
21969              /**
21970              * @event push
21971              * Fires when the iframe editor is updated with content from the textarea.
21972              * @param {HtmlEditor} this
21973              * @param {String} html
21974              */
21975             push: true,
21976              /**
21977              * @event editmodechange
21978              * Fires when the editor switches edit modes
21979              * @param {HtmlEditor} this
21980              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21981              */
21982             editmodechange: true,
21983             /**
21984              * @event editorevent
21985              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21986              * @param {HtmlEditor} this
21987              */
21988             editorevent: true,
21989             /**
21990              * @event firstfocus
21991              * Fires when on first focus - needed by toolbars..
21992              * @param {HtmlEditor} this
21993              */
21994             firstfocus: true,
21995             /**
21996              * @event autosave
21997              * Auto save the htmlEditor value as a file into Events
21998              * @param {HtmlEditor} this
21999              */
22000             autosave: true,
22001             /**
22002              * @event savedpreview
22003              * preview the saved version of htmlEditor
22004              * @param {HtmlEditor} this
22005              */
22006             savedpreview: true
22007         });
22008 };
22009
22010
22011 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22012     
22013     
22014       /**
22015      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22016      */
22017     toolbars : false,
22018    
22019      /**
22020      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22021      *                        Roo.resizable.
22022      */
22023     resizable : false,
22024      /**
22025      * @cfg {Number} height (in pixels)
22026      */   
22027     height: 300,
22028    /**
22029      * @cfg {Number} width (in pixels)
22030      */   
22031     width: false,
22032     
22033     /**
22034      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22035      * 
22036      */
22037     stylesheets: false,
22038     
22039     // id of frame..
22040     frameId: false,
22041     
22042     // private properties
22043     validationEvent : false,
22044     deferHeight: true,
22045     initialized : false,
22046     activated : false,
22047     
22048     onFocus : Roo.emptyFn,
22049     iframePad:3,
22050     hideMode:'offsets',
22051     
22052     
22053     tbContainer : false,
22054     
22055     toolbarContainer :function() {
22056         return this.wrap.select('.x-html-editor-tb',true).first();
22057     },
22058
22059     /**
22060      * Protected method that will not generally be called directly. It
22061      * is called when the editor creates its toolbar. Override this method if you need to
22062      * add custom toolbar buttons.
22063      * @param {HtmlEditor} editor
22064      */
22065     createToolbar : function(){
22066         
22067         Roo.log("create toolbars");
22068         
22069         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22070         this.toolbars[0].render(this.toolbarContainer());
22071         
22072         return;
22073         
22074 //        if (!editor.toolbars || !editor.toolbars.length) {
22075 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22076 //        }
22077 //        
22078 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22079 //            editor.toolbars[i] = Roo.factory(
22080 //                    typeof(editor.toolbars[i]) == 'string' ?
22081 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22082 //                Roo.bootstrap.HtmlEditor);
22083 //            editor.toolbars[i].init(editor);
22084 //        }
22085     },
22086
22087      
22088     // private
22089     onRender : function(ct, position)
22090     {
22091        // Roo.log("Call onRender: " + this.xtype);
22092         var _t = this;
22093         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22094       
22095         this.wrap = this.inputEl().wrap({
22096             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22097         });
22098         
22099         this.editorcore.onRender(ct, position);
22100          
22101         if (this.resizable) {
22102             this.resizeEl = new Roo.Resizable(this.wrap, {
22103                 pinned : true,
22104                 wrap: true,
22105                 dynamic : true,
22106                 minHeight : this.height,
22107                 height: this.height,
22108                 handles : this.resizable,
22109                 width: this.width,
22110                 listeners : {
22111                     resize : function(r, w, h) {
22112                         _t.onResize(w,h); // -something
22113                     }
22114                 }
22115             });
22116             
22117         }
22118         this.createToolbar(this);
22119        
22120         
22121         if(!this.width && this.resizable){
22122             this.setSize(this.wrap.getSize());
22123         }
22124         if (this.resizeEl) {
22125             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22126             // should trigger onReize..
22127         }
22128         
22129     },
22130
22131     // private
22132     onResize : function(w, h)
22133     {
22134         Roo.log('resize: ' +w + ',' + h );
22135         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22136         var ew = false;
22137         var eh = false;
22138         
22139         if(this.inputEl() ){
22140             if(typeof w == 'number'){
22141                 var aw = w - this.wrap.getFrameWidth('lr');
22142                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22143                 ew = aw;
22144             }
22145             if(typeof h == 'number'){
22146                  var tbh = -11;  // fixme it needs to tool bar size!
22147                 for (var i =0; i < this.toolbars.length;i++) {
22148                     // fixme - ask toolbars for heights?
22149                     tbh += this.toolbars[i].el.getHeight();
22150                     //if (this.toolbars[i].footer) {
22151                     //    tbh += this.toolbars[i].footer.el.getHeight();
22152                     //}
22153                 }
22154               
22155                 
22156                 
22157                 
22158                 
22159                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22160                 ah -= 5; // knock a few pixes off for look..
22161                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22162                 var eh = ah;
22163             }
22164         }
22165         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22166         this.editorcore.onResize(ew,eh);
22167         
22168     },
22169
22170     /**
22171      * Toggles the editor between standard and source edit mode.
22172      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22173      */
22174     toggleSourceEdit : function(sourceEditMode)
22175     {
22176         this.editorcore.toggleSourceEdit(sourceEditMode);
22177         
22178         if(this.editorcore.sourceEditMode){
22179             Roo.log('editor - showing textarea');
22180             
22181 //            Roo.log('in');
22182 //            Roo.log(this.syncValue());
22183             this.syncValue();
22184             this.inputEl().removeClass(['hide', 'x-hidden']);
22185             this.inputEl().dom.removeAttribute('tabIndex');
22186             this.inputEl().focus();
22187         }else{
22188             Roo.log('editor - hiding textarea');
22189 //            Roo.log('out')
22190 //            Roo.log(this.pushValue()); 
22191             this.pushValue();
22192             
22193             this.inputEl().addClass(['hide', 'x-hidden']);
22194             this.inputEl().dom.setAttribute('tabIndex', -1);
22195             //this.deferFocus();
22196         }
22197          
22198         if(this.resizable){
22199             this.setSize(this.wrap.getSize());
22200         }
22201         
22202         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22203     },
22204  
22205     // private (for BoxComponent)
22206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22207
22208     // private (for BoxComponent)
22209     getResizeEl : function(){
22210         return this.wrap;
22211     },
22212
22213     // private (for BoxComponent)
22214     getPositionEl : function(){
22215         return this.wrap;
22216     },
22217
22218     // private
22219     initEvents : function(){
22220         this.originalValue = this.getValue();
22221     },
22222
22223 //    /**
22224 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22225 //     * @method
22226 //     */
22227 //    markInvalid : Roo.emptyFn,
22228 //    /**
22229 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22230 //     * @method
22231 //     */
22232 //    clearInvalid : Roo.emptyFn,
22233
22234     setValue : function(v){
22235         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22236         this.editorcore.pushValue();
22237     },
22238
22239      
22240     // private
22241     deferFocus : function(){
22242         this.focus.defer(10, this);
22243     },
22244
22245     // doc'ed in Field
22246     focus : function(){
22247         this.editorcore.focus();
22248         
22249     },
22250       
22251
22252     // private
22253     onDestroy : function(){
22254         
22255         
22256         
22257         if(this.rendered){
22258             
22259             for (var i =0; i < this.toolbars.length;i++) {
22260                 // fixme - ask toolbars for heights?
22261                 this.toolbars[i].onDestroy();
22262             }
22263             
22264             this.wrap.dom.innerHTML = '';
22265             this.wrap.remove();
22266         }
22267     },
22268
22269     // private
22270     onFirstFocus : function(){
22271         //Roo.log("onFirstFocus");
22272         this.editorcore.onFirstFocus();
22273          for (var i =0; i < this.toolbars.length;i++) {
22274             this.toolbars[i].onFirstFocus();
22275         }
22276         
22277     },
22278     
22279     // private
22280     syncValue : function()
22281     {   
22282         this.editorcore.syncValue();
22283     },
22284     
22285     pushValue : function()
22286     {   
22287         this.editorcore.pushValue();
22288     }
22289      
22290     
22291     // hide stuff that is not compatible
22292     /**
22293      * @event blur
22294      * @hide
22295      */
22296     /**
22297      * @event change
22298      * @hide
22299      */
22300     /**
22301      * @event focus
22302      * @hide
22303      */
22304     /**
22305      * @event specialkey
22306      * @hide
22307      */
22308     /**
22309      * @cfg {String} fieldClass @hide
22310      */
22311     /**
22312      * @cfg {String} focusClass @hide
22313      */
22314     /**
22315      * @cfg {String} autoCreate @hide
22316      */
22317     /**
22318      * @cfg {String} inputType @hide
22319      */
22320     /**
22321      * @cfg {String} invalidClass @hide
22322      */
22323     /**
22324      * @cfg {String} invalidText @hide
22325      */
22326     /**
22327      * @cfg {String} msgFx @hide
22328      */
22329     /**
22330      * @cfg {String} validateOnBlur @hide
22331      */
22332 });
22333  
22334     
22335    
22336    
22337    
22338       
22339 Roo.namespace('Roo.bootstrap.htmleditor');
22340 /**
22341  * @class Roo.bootstrap.HtmlEditorToolbar1
22342  * Basic Toolbar
22343  * 
22344  * Usage:
22345  *
22346  new Roo.bootstrap.HtmlEditor({
22347     ....
22348     toolbars : [
22349         new Roo.bootstrap.HtmlEditorToolbar1({
22350             disable : { fonts: 1 , format: 1, ..., ... , ...],
22351             btns : [ .... ]
22352         })
22353     }
22354      
22355  * 
22356  * @cfg {Object} disable List of elements to disable..
22357  * @cfg {Array} btns List of additional buttons.
22358  * 
22359  * 
22360  * NEEDS Extra CSS? 
22361  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22362  */
22363  
22364 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22365 {
22366     
22367     Roo.apply(this, config);
22368     
22369     // default disabled, based on 'good practice'..
22370     this.disable = this.disable || {};
22371     Roo.applyIf(this.disable, {
22372         fontSize : true,
22373         colors : true,
22374         specialElements : true
22375     });
22376     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22377     
22378     this.editor = config.editor;
22379     this.editorcore = config.editor.editorcore;
22380     
22381     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22382     
22383     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22384     // dont call parent... till later.
22385 }
22386 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22387      
22388     bar : true,
22389     
22390     editor : false,
22391     editorcore : false,
22392     
22393     
22394     formats : [
22395         "p" ,  
22396         "h1","h2","h3","h4","h5","h6", 
22397         "pre", "code", 
22398         "abbr", "acronym", "address", "cite", "samp", "var",
22399         'div','span'
22400     ],
22401     
22402     onRender : function(ct, position)
22403     {
22404        // Roo.log("Call onRender: " + this.xtype);
22405         
22406        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22407        Roo.log(this.el);
22408        this.el.dom.style.marginBottom = '0';
22409        var _this = this;
22410        var editorcore = this.editorcore;
22411        var editor= this.editor;
22412        
22413        var children = [];
22414        var btn = function(id,cmd , toggle, handler){
22415        
22416             var  event = toggle ? 'toggle' : 'click';
22417        
22418             var a = {
22419                 size : 'sm',
22420                 xtype: 'Button',
22421                 xns: Roo.bootstrap,
22422                 glyphicon : id,
22423                 cmd : id || cmd,
22424                 enableToggle:toggle !== false,
22425                 //html : 'submit'
22426                 pressed : toggle ? false : null,
22427                 listeners : {}
22428             };
22429             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22430                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22431             };
22432             children.push(a);
22433             return a;
22434        }
22435         
22436         var style = {
22437                 xtype: 'Button',
22438                 size : 'sm',
22439                 xns: Roo.bootstrap,
22440                 glyphicon : 'font',
22441                 //html : 'submit'
22442                 menu : {
22443                     xtype: 'Menu',
22444                     xns: Roo.bootstrap,
22445                     items:  []
22446                 }
22447         };
22448         Roo.each(this.formats, function(f) {
22449             style.menu.items.push({
22450                 xtype :'MenuItem',
22451                 xns: Roo.bootstrap,
22452                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22453                 tagname : f,
22454                 listeners : {
22455                     click : function()
22456                     {
22457                         editorcore.insertTag(this.tagname);
22458                         editor.focus();
22459                     }
22460                 }
22461                 
22462             });
22463         });
22464          children.push(style);   
22465             
22466             
22467         btn('bold',false,true);
22468         btn('italic',false,true);
22469         btn('align-left', 'justifyleft',true);
22470         btn('align-center', 'justifycenter',true);
22471         btn('align-right' , 'justifyright',true);
22472         btn('link', false, false, function(btn) {
22473             //Roo.log("create link?");
22474             var url = prompt(this.createLinkText, this.defaultLinkValue);
22475             if(url && url != 'http:/'+'/'){
22476                 this.editorcore.relayCmd('createlink', url);
22477             }
22478         }),
22479         btn('list','insertunorderedlist',true);
22480         btn('pencil', false,true, function(btn){
22481                 Roo.log(this);
22482                 
22483                 this.toggleSourceEdit(btn.pressed);
22484         });
22485         /*
22486         var cog = {
22487                 xtype: 'Button',
22488                 size : 'sm',
22489                 xns: Roo.bootstrap,
22490                 glyphicon : 'cog',
22491                 //html : 'submit'
22492                 menu : {
22493                     xtype: 'Menu',
22494                     xns: Roo.bootstrap,
22495                     items:  []
22496                 }
22497         };
22498         
22499         cog.menu.items.push({
22500             xtype :'MenuItem',
22501             xns: Roo.bootstrap,
22502             html : Clean styles,
22503             tagname : f,
22504             listeners : {
22505                 click : function()
22506                 {
22507                     editorcore.insertTag(this.tagname);
22508                     editor.focus();
22509                 }
22510             }
22511             
22512         });
22513        */
22514         
22515          
22516        this.xtype = 'NavSimplebar';
22517         
22518         for(var i=0;i< children.length;i++) {
22519             
22520             this.buttons.add(this.addxtypeChild(children[i]));
22521             
22522         }
22523         
22524         editor.on('editorevent', this.updateToolbar, this);
22525     },
22526     onBtnClick : function(id)
22527     {
22528        this.editorcore.relayCmd(id);
22529        this.editorcore.focus();
22530     },
22531     
22532     /**
22533      * Protected method that will not generally be called directly. It triggers
22534      * a toolbar update by reading the markup state of the current selection in the editor.
22535      */
22536     updateToolbar: function(){
22537
22538         if(!this.editorcore.activated){
22539             this.editor.onFirstFocus(); // is this neeed?
22540             return;
22541         }
22542
22543         var btns = this.buttons; 
22544         var doc = this.editorcore.doc;
22545         btns.get('bold').setActive(doc.queryCommandState('bold'));
22546         btns.get('italic').setActive(doc.queryCommandState('italic'));
22547         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22548         
22549         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22550         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22551         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22552         
22553         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22554         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22555          /*
22556         
22557         var ans = this.editorcore.getAllAncestors();
22558         if (this.formatCombo) {
22559             
22560             
22561             var store = this.formatCombo.store;
22562             this.formatCombo.setValue("");
22563             for (var i =0; i < ans.length;i++) {
22564                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22565                     // select it..
22566                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22567                     break;
22568                 }
22569             }
22570         }
22571         
22572         
22573         
22574         // hides menus... - so this cant be on a menu...
22575         Roo.bootstrap.MenuMgr.hideAll();
22576         */
22577         Roo.bootstrap.MenuMgr.hideAll();
22578         //this.editorsyncValue();
22579     },
22580     onFirstFocus: function() {
22581         this.buttons.each(function(item){
22582            item.enable();
22583         });
22584     },
22585     toggleSourceEdit : function(sourceEditMode){
22586         
22587           
22588         if(sourceEditMode){
22589             Roo.log("disabling buttons");
22590            this.buttons.each( function(item){
22591                 if(item.cmd != 'pencil'){
22592                     item.disable();
22593                 }
22594             });
22595           
22596         }else{
22597             Roo.log("enabling buttons");
22598             if(this.editorcore.initialized){
22599                 this.buttons.each( function(item){
22600                     item.enable();
22601                 });
22602             }
22603             
22604         }
22605         Roo.log("calling toggole on editor");
22606         // tell the editor that it's been pressed..
22607         this.editor.toggleSourceEdit(sourceEditMode);
22608        
22609     }
22610 });
22611
22612
22613
22614
22615
22616 /**
22617  * @class Roo.bootstrap.Table.AbstractSelectionModel
22618  * @extends Roo.util.Observable
22619  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22620  * implemented by descendant classes.  This class should not be directly instantiated.
22621  * @constructor
22622  */
22623 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22624     this.locked = false;
22625     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22626 };
22627
22628
22629 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22630     /** @ignore Called by the grid automatically. Do not call directly. */
22631     init : function(grid){
22632         this.grid = grid;
22633         this.initEvents();
22634     },
22635
22636     /**
22637      * Locks the selections.
22638      */
22639     lock : function(){
22640         this.locked = true;
22641     },
22642
22643     /**
22644      * Unlocks the selections.
22645      */
22646     unlock : function(){
22647         this.locked = false;
22648     },
22649
22650     /**
22651      * Returns true if the selections are locked.
22652      * @return {Boolean}
22653      */
22654     isLocked : function(){
22655         return this.locked;
22656     }
22657 });
22658 /**
22659  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22660  * @class Roo.bootstrap.Table.RowSelectionModel
22661  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22662  * It supports multiple selections and keyboard selection/navigation. 
22663  * @constructor
22664  * @param {Object} config
22665  */
22666
22667 Roo.bootstrap.Table.RowSelectionModel = function(config){
22668     Roo.apply(this, config);
22669     this.selections = new Roo.util.MixedCollection(false, function(o){
22670         return o.id;
22671     });
22672
22673     this.last = false;
22674     this.lastActive = false;
22675
22676     this.addEvents({
22677         /**
22678              * @event selectionchange
22679              * Fires when the selection changes
22680              * @param {SelectionModel} this
22681              */
22682             "selectionchange" : true,
22683         /**
22684              * @event afterselectionchange
22685              * Fires after the selection changes (eg. by key press or clicking)
22686              * @param {SelectionModel} this
22687              */
22688             "afterselectionchange" : true,
22689         /**
22690              * @event beforerowselect
22691              * Fires when a row is selected being selected, return false to cancel.
22692              * @param {SelectionModel} this
22693              * @param {Number} rowIndex The selected index
22694              * @param {Boolean} keepExisting False if other selections will be cleared
22695              */
22696             "beforerowselect" : true,
22697         /**
22698              * @event rowselect
22699              * Fires when a row is selected.
22700              * @param {SelectionModel} this
22701              * @param {Number} rowIndex The selected index
22702              * @param {Roo.data.Record} r The record
22703              */
22704             "rowselect" : true,
22705         /**
22706              * @event rowdeselect
22707              * Fires when a row is deselected.
22708              * @param {SelectionModel} this
22709              * @param {Number} rowIndex The selected index
22710              */
22711         "rowdeselect" : true
22712     });
22713     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22714     this.locked = false;
22715  };
22716
22717 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22718     /**
22719      * @cfg {Boolean} singleSelect
22720      * True to allow selection of only one row at a time (defaults to false)
22721      */
22722     singleSelect : false,
22723
22724     // private
22725     initEvents : function()
22726     {
22727
22728         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22729         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22730         //}else{ // allow click to work like normal
22731          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22732         //}
22733         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22734         this.grid.on("rowclick", this.handleMouseDown, this);
22735         
22736         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22737             "up" : function(e){
22738                 if(!e.shiftKey){
22739                     this.selectPrevious(e.shiftKey);
22740                 }else if(this.last !== false && this.lastActive !== false){
22741                     var last = this.last;
22742                     this.selectRange(this.last,  this.lastActive-1);
22743                     this.grid.getView().focusRow(this.lastActive);
22744                     if(last !== false){
22745                         this.last = last;
22746                     }
22747                 }else{
22748                     this.selectFirstRow();
22749                 }
22750                 this.fireEvent("afterselectionchange", this);
22751             },
22752             "down" : function(e){
22753                 if(!e.shiftKey){
22754                     this.selectNext(e.shiftKey);
22755                 }else if(this.last !== false && this.lastActive !== false){
22756                     var last = this.last;
22757                     this.selectRange(this.last,  this.lastActive+1);
22758                     this.grid.getView().focusRow(this.lastActive);
22759                     if(last !== false){
22760                         this.last = last;
22761                     }
22762                 }else{
22763                     this.selectFirstRow();
22764                 }
22765                 this.fireEvent("afterselectionchange", this);
22766             },
22767             scope: this
22768         });
22769         this.grid.store.on('load', function(){
22770             this.selections.clear();
22771         },this);
22772         /*
22773         var view = this.grid.view;
22774         view.on("refresh", this.onRefresh, this);
22775         view.on("rowupdated", this.onRowUpdated, this);
22776         view.on("rowremoved", this.onRemove, this);
22777         */
22778     },
22779
22780     // private
22781     onRefresh : function()
22782     {
22783         var ds = this.grid.store, i, v = this.grid.view;
22784         var s = this.selections;
22785         s.each(function(r){
22786             if((i = ds.indexOfId(r.id)) != -1){
22787                 v.onRowSelect(i);
22788             }else{
22789                 s.remove(r);
22790             }
22791         });
22792     },
22793
22794     // private
22795     onRemove : function(v, index, r){
22796         this.selections.remove(r);
22797     },
22798
22799     // private
22800     onRowUpdated : function(v, index, r){
22801         if(this.isSelected(r)){
22802             v.onRowSelect(index);
22803         }
22804     },
22805
22806     /**
22807      * Select records.
22808      * @param {Array} records The records to select
22809      * @param {Boolean} keepExisting (optional) True to keep existing selections
22810      */
22811     selectRecords : function(records, keepExisting)
22812     {
22813         if(!keepExisting){
22814             this.clearSelections();
22815         }
22816             var ds = this.grid.store;
22817         for(var i = 0, len = records.length; i < len; i++){
22818             this.selectRow(ds.indexOf(records[i]), true);
22819         }
22820     },
22821
22822     /**
22823      * Gets the number of selected rows.
22824      * @return {Number}
22825      */
22826     getCount : function(){
22827         return this.selections.length;
22828     },
22829
22830     /**
22831      * Selects the first row in the grid.
22832      */
22833     selectFirstRow : function(){
22834         this.selectRow(0);
22835     },
22836
22837     /**
22838      * Select the last row.
22839      * @param {Boolean} keepExisting (optional) True to keep existing selections
22840      */
22841     selectLastRow : function(keepExisting){
22842         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22843         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22844     },
22845
22846     /**
22847      * Selects the row immediately following the last selected row.
22848      * @param {Boolean} keepExisting (optional) True to keep existing selections
22849      */
22850     selectNext : function(keepExisting)
22851     {
22852             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22853             this.selectRow(this.last+1, keepExisting);
22854             this.grid.getView().focusRow(this.last);
22855         }
22856     },
22857
22858     /**
22859      * Selects the row that precedes the last selected row.
22860      * @param {Boolean} keepExisting (optional) True to keep existing selections
22861      */
22862     selectPrevious : function(keepExisting){
22863         if(this.last){
22864             this.selectRow(this.last-1, keepExisting);
22865             this.grid.getView().focusRow(this.last);
22866         }
22867     },
22868
22869     /**
22870      * Returns the selected records
22871      * @return {Array} Array of selected records
22872      */
22873     getSelections : function(){
22874         return [].concat(this.selections.items);
22875     },
22876
22877     /**
22878      * Returns the first selected record.
22879      * @return {Record}
22880      */
22881     getSelected : function(){
22882         return this.selections.itemAt(0);
22883     },
22884
22885
22886     /**
22887      * Clears all selections.
22888      */
22889     clearSelections : function(fast)
22890     {
22891         if(this.locked) {
22892             return;
22893         }
22894         if(fast !== true){
22895                 var ds = this.grid.store;
22896             var s = this.selections;
22897             s.each(function(r){
22898                 this.deselectRow(ds.indexOfId(r.id));
22899             }, this);
22900             s.clear();
22901         }else{
22902             this.selections.clear();
22903         }
22904         this.last = false;
22905     },
22906
22907
22908     /**
22909      * Selects all rows.
22910      */
22911     selectAll : function(){
22912         if(this.locked) {
22913             return;
22914         }
22915         this.selections.clear();
22916         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22917             this.selectRow(i, true);
22918         }
22919     },
22920
22921     /**
22922      * Returns True if there is a selection.
22923      * @return {Boolean}
22924      */
22925     hasSelection : function(){
22926         return this.selections.length > 0;
22927     },
22928
22929     /**
22930      * Returns True if the specified row is selected.
22931      * @param {Number/Record} record The record or index of the record to check
22932      * @return {Boolean}
22933      */
22934     isSelected : function(index){
22935             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22936         return (r && this.selections.key(r.id) ? true : false);
22937     },
22938
22939     /**
22940      * Returns True if the specified record id is selected.
22941      * @param {String} id The id of record to check
22942      * @return {Boolean}
22943      */
22944     isIdSelected : function(id){
22945         return (this.selections.key(id) ? true : false);
22946     },
22947
22948
22949     // private
22950     handleMouseDBClick : function(e, t){
22951         
22952     },
22953     // private
22954     handleMouseDown : function(e, t)
22955     {
22956             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22957         if(this.isLocked() || rowIndex < 0 ){
22958             return;
22959         };
22960         if(e.shiftKey && this.last !== false){
22961             var last = this.last;
22962             this.selectRange(last, rowIndex, e.ctrlKey);
22963             this.last = last; // reset the last
22964             t.focus();
22965     
22966         }else{
22967             var isSelected = this.isSelected(rowIndex);
22968             //Roo.log("select row:" + rowIndex);
22969             if(isSelected){
22970                 this.deselectRow(rowIndex);
22971             } else {
22972                         this.selectRow(rowIndex, true);
22973             }
22974     
22975             /*
22976                 if(e.button !== 0 && isSelected){
22977                 alert('rowIndex 2: ' + rowIndex);
22978                     view.focusRow(rowIndex);
22979                 }else if(e.ctrlKey && isSelected){
22980                     this.deselectRow(rowIndex);
22981                 }else if(!isSelected){
22982                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22983                     view.focusRow(rowIndex);
22984                 }
22985             */
22986         }
22987         this.fireEvent("afterselectionchange", this);
22988     },
22989     // private
22990     handleDragableRowClick :  function(grid, rowIndex, e) 
22991     {
22992         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22993             this.selectRow(rowIndex, false);
22994             grid.view.focusRow(rowIndex);
22995              this.fireEvent("afterselectionchange", this);
22996         }
22997     },
22998     
22999     /**
23000      * Selects multiple rows.
23001      * @param {Array} rows Array of the indexes of the row to select
23002      * @param {Boolean} keepExisting (optional) True to keep existing selections
23003      */
23004     selectRows : function(rows, keepExisting){
23005         if(!keepExisting){
23006             this.clearSelections();
23007         }
23008         for(var i = 0, len = rows.length; i < len; i++){
23009             this.selectRow(rows[i], true);
23010         }
23011     },
23012
23013     /**
23014      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23015      * @param {Number} startRow The index of the first row in the range
23016      * @param {Number} endRow The index of the last row in the range
23017      * @param {Boolean} keepExisting (optional) True to retain existing selections
23018      */
23019     selectRange : function(startRow, endRow, keepExisting){
23020         if(this.locked) {
23021             return;
23022         }
23023         if(!keepExisting){
23024             this.clearSelections();
23025         }
23026         if(startRow <= endRow){
23027             for(var i = startRow; i <= endRow; i++){
23028                 this.selectRow(i, true);
23029             }
23030         }else{
23031             for(var i = startRow; i >= endRow; i--){
23032                 this.selectRow(i, true);
23033             }
23034         }
23035     },
23036
23037     /**
23038      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23039      * @param {Number} startRow The index of the first row in the range
23040      * @param {Number} endRow The index of the last row in the range
23041      */
23042     deselectRange : function(startRow, endRow, preventViewNotify){
23043         if(this.locked) {
23044             return;
23045         }
23046         for(var i = startRow; i <= endRow; i++){
23047             this.deselectRow(i, preventViewNotify);
23048         }
23049     },
23050
23051     /**
23052      * Selects a row.
23053      * @param {Number} row The index of the row to select
23054      * @param {Boolean} keepExisting (optional) True to keep existing selections
23055      */
23056     selectRow : function(index, keepExisting, preventViewNotify)
23057     {
23058             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23059             return;
23060         }
23061         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23062             if(!keepExisting || this.singleSelect){
23063                 this.clearSelections();
23064             }
23065             
23066             var r = this.grid.store.getAt(index);
23067             //console.log('selectRow - record id :' + r.id);
23068             
23069             this.selections.add(r);
23070             this.last = this.lastActive = index;
23071             if(!preventViewNotify){
23072                 var proxy = new Roo.Element(
23073                                 this.grid.getRowDom(index)
23074                 );
23075                 proxy.addClass('bg-info info');
23076             }
23077             this.fireEvent("rowselect", this, index, r);
23078             this.fireEvent("selectionchange", this);
23079         }
23080     },
23081
23082     /**
23083      * Deselects a row.
23084      * @param {Number} row The index of the row to deselect
23085      */
23086     deselectRow : function(index, preventViewNotify)
23087     {
23088         if(this.locked) {
23089             return;
23090         }
23091         if(this.last == index){
23092             this.last = false;
23093         }
23094         if(this.lastActive == index){
23095             this.lastActive = false;
23096         }
23097         
23098         var r = this.grid.store.getAt(index);
23099         if (!r) {
23100             return;
23101         }
23102         
23103         this.selections.remove(r);
23104         //.console.log('deselectRow - record id :' + r.id);
23105         if(!preventViewNotify){
23106         
23107             var proxy = new Roo.Element(
23108                 this.grid.getRowDom(index)
23109             );
23110             proxy.removeClass('bg-info info');
23111         }
23112         this.fireEvent("rowdeselect", this, index);
23113         this.fireEvent("selectionchange", this);
23114     },
23115
23116     // private
23117     restoreLast : function(){
23118         if(this._last){
23119             this.last = this._last;
23120         }
23121     },
23122
23123     // private
23124     acceptsNav : function(row, col, cm){
23125         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23126     },
23127
23128     // private
23129     onEditorKey : function(field, e){
23130         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23131         if(k == e.TAB){
23132             e.stopEvent();
23133             ed.completeEdit();
23134             if(e.shiftKey){
23135                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23136             }else{
23137                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23138             }
23139         }else if(k == e.ENTER && !e.ctrlKey){
23140             e.stopEvent();
23141             ed.completeEdit();
23142             if(e.shiftKey){
23143                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23144             }else{
23145                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23146             }
23147         }else if(k == e.ESC){
23148             ed.cancelEdit();
23149         }
23150         if(newCell){
23151             g.startEditing(newCell[0], newCell[1]);
23152         }
23153     }
23154 });
23155 /*
23156  * Based on:
23157  * Ext JS Library 1.1.1
23158  * Copyright(c) 2006-2007, Ext JS, LLC.
23159  *
23160  * Originally Released Under LGPL - original licence link has changed is not relivant.
23161  *
23162  * Fork - LGPL
23163  * <script type="text/javascript">
23164  */
23165  
23166 /**
23167  * @class Roo.bootstrap.PagingToolbar
23168  * @extends Roo.bootstrap.NavSimplebar
23169  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23170  * @constructor
23171  * Create a new PagingToolbar
23172  * @param {Object} config The config object
23173  * @param {Roo.data.Store} store
23174  */
23175 Roo.bootstrap.PagingToolbar = function(config)
23176 {
23177     // old args format still supported... - xtype is prefered..
23178         // created from xtype...
23179     
23180     this.ds = config.dataSource;
23181     
23182     if (config.store && !this.ds) {
23183         this.store= Roo.factory(config.store, Roo.data);
23184         this.ds = this.store;
23185         this.ds.xmodule = this.xmodule || false;
23186     }
23187     
23188     this.toolbarItems = [];
23189     if (config.items) {
23190         this.toolbarItems = config.items;
23191     }
23192     
23193     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23194     
23195     this.cursor = 0;
23196     
23197     if (this.ds) { 
23198         this.bind(this.ds);
23199     }
23200     
23201     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23202     
23203 };
23204
23205 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23206     /**
23207      * @cfg {Roo.data.Store} dataSource
23208      * The underlying data store providing the paged data
23209      */
23210     /**
23211      * @cfg {String/HTMLElement/Element} container
23212      * container The id or element that will contain the toolbar
23213      */
23214     /**
23215      * @cfg {Boolean} displayInfo
23216      * True to display the displayMsg (defaults to false)
23217      */
23218     /**
23219      * @cfg {Number} pageSize
23220      * The number of records to display per page (defaults to 20)
23221      */
23222     pageSize: 20,
23223     /**
23224      * @cfg {String} displayMsg
23225      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23226      */
23227     displayMsg : 'Displaying {0} - {1} of {2}',
23228     /**
23229      * @cfg {String} emptyMsg
23230      * The message to display when no records are found (defaults to "No data to display")
23231      */
23232     emptyMsg : 'No data to display',
23233     /**
23234      * Customizable piece of the default paging text (defaults to "Page")
23235      * @type String
23236      */
23237     beforePageText : "Page",
23238     /**
23239      * Customizable piece of the default paging text (defaults to "of %0")
23240      * @type String
23241      */
23242     afterPageText : "of {0}",
23243     /**
23244      * Customizable piece of the default paging text (defaults to "First Page")
23245      * @type String
23246      */
23247     firstText : "First Page",
23248     /**
23249      * Customizable piece of the default paging text (defaults to "Previous Page")
23250      * @type String
23251      */
23252     prevText : "Previous Page",
23253     /**
23254      * Customizable piece of the default paging text (defaults to "Next Page")
23255      * @type String
23256      */
23257     nextText : "Next Page",
23258     /**
23259      * Customizable piece of the default paging text (defaults to "Last Page")
23260      * @type String
23261      */
23262     lastText : "Last Page",
23263     /**
23264      * Customizable piece of the default paging text (defaults to "Refresh")
23265      * @type String
23266      */
23267     refreshText : "Refresh",
23268
23269     buttons : false,
23270     // private
23271     onRender : function(ct, position) 
23272     {
23273         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23274         this.navgroup.parentId = this.id;
23275         this.navgroup.onRender(this.el, null);
23276         // add the buttons to the navgroup
23277         
23278         if(this.displayInfo){
23279             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23280             this.displayEl = this.el.select('.x-paging-info', true).first();
23281 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23282 //            this.displayEl = navel.el.select('span',true).first();
23283         }
23284         
23285         var _this = this;
23286         
23287         if(this.buttons){
23288             Roo.each(_this.buttons, function(e){ // this might need to use render????
23289                Roo.factory(e).onRender(_this.el, null);
23290             });
23291         }
23292             
23293         Roo.each(_this.toolbarItems, function(e) {
23294             _this.navgroup.addItem(e);
23295         });
23296         
23297         
23298         this.first = this.navgroup.addItem({
23299             tooltip: this.firstText,
23300             cls: "prev",
23301             icon : 'fa fa-backward',
23302             disabled: true,
23303             preventDefault: true,
23304             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23305         });
23306         
23307         this.prev =  this.navgroup.addItem({
23308             tooltip: this.prevText,
23309             cls: "prev",
23310             icon : 'fa fa-step-backward',
23311             disabled: true,
23312             preventDefault: true,
23313             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23314         });
23315     //this.addSeparator();
23316         
23317         
23318         var field = this.navgroup.addItem( {
23319             tagtype : 'span',
23320             cls : 'x-paging-position',
23321             
23322             html : this.beforePageText  +
23323                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23324                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23325          } ); //?? escaped?
23326         
23327         this.field = field.el.select('input', true).first();
23328         this.field.on("keydown", this.onPagingKeydown, this);
23329         this.field.on("focus", function(){this.dom.select();});
23330     
23331     
23332         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23333         //this.field.setHeight(18);
23334         //this.addSeparator();
23335         this.next = this.navgroup.addItem({
23336             tooltip: this.nextText,
23337             cls: "next",
23338             html : ' <i class="fa fa-step-forward">',
23339             disabled: true,
23340             preventDefault: true,
23341             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23342         });
23343         this.last = this.navgroup.addItem({
23344             tooltip: this.lastText,
23345             icon : 'fa fa-forward',
23346             cls: "next",
23347             disabled: true,
23348             preventDefault: true,
23349             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23350         });
23351     //this.addSeparator();
23352         this.loading = this.navgroup.addItem({
23353             tooltip: this.refreshText,
23354             icon: 'fa fa-refresh',
23355             preventDefault: true,
23356             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23357         });
23358         
23359     },
23360
23361     // private
23362     updateInfo : function(){
23363         if(this.displayEl){
23364             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23365             var msg = count == 0 ?
23366                 this.emptyMsg :
23367                 String.format(
23368                     this.displayMsg,
23369                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23370                 );
23371             this.displayEl.update(msg);
23372         }
23373     },
23374
23375     // private
23376     onLoad : function(ds, r, o){
23377        this.cursor = o.params ? o.params.start : 0;
23378        var d = this.getPageData(),
23379             ap = d.activePage,
23380             ps = d.pages;
23381         
23382        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23383        this.field.dom.value = ap;
23384        this.first.setDisabled(ap == 1);
23385        this.prev.setDisabled(ap == 1);
23386        this.next.setDisabled(ap == ps);
23387        this.last.setDisabled(ap == ps);
23388        this.loading.enable();
23389        this.updateInfo();
23390     },
23391
23392     // private
23393     getPageData : function(){
23394         var total = this.ds.getTotalCount();
23395         return {
23396             total : total,
23397             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23398             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23399         };
23400     },
23401
23402     // private
23403     onLoadError : function(){
23404         this.loading.enable();
23405     },
23406
23407     // private
23408     onPagingKeydown : function(e){
23409         var k = e.getKey();
23410         var d = this.getPageData();
23411         if(k == e.RETURN){
23412             var v = this.field.dom.value, pageNum;
23413             if(!v || isNaN(pageNum = parseInt(v, 10))){
23414                 this.field.dom.value = d.activePage;
23415                 return;
23416             }
23417             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23418             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23419             e.stopEvent();
23420         }
23421         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))
23422         {
23423           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23424           this.field.dom.value = pageNum;
23425           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23426           e.stopEvent();
23427         }
23428         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23429         {
23430           var v = this.field.dom.value, pageNum; 
23431           var increment = (e.shiftKey) ? 10 : 1;
23432           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23433                 increment *= -1;
23434           }
23435           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23436             this.field.dom.value = d.activePage;
23437             return;
23438           }
23439           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23440           {
23441             this.field.dom.value = parseInt(v, 10) + increment;
23442             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23443             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23444           }
23445           e.stopEvent();
23446         }
23447     },
23448
23449     // private
23450     beforeLoad : function(){
23451         if(this.loading){
23452             this.loading.disable();
23453         }
23454     },
23455
23456     // private
23457     onClick : function(which){
23458         
23459         var ds = this.ds;
23460         if (!ds) {
23461             return;
23462         }
23463         
23464         switch(which){
23465             case "first":
23466                 ds.load({params:{start: 0, limit: this.pageSize}});
23467             break;
23468             case "prev":
23469                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23470             break;
23471             case "next":
23472                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23473             break;
23474             case "last":
23475                 var total = ds.getTotalCount();
23476                 var extra = total % this.pageSize;
23477                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23478                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23479             break;
23480             case "refresh":
23481                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23482             break;
23483         }
23484     },
23485
23486     /**
23487      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23488      * @param {Roo.data.Store} store The data store to unbind
23489      */
23490     unbind : function(ds){
23491         ds.un("beforeload", this.beforeLoad, this);
23492         ds.un("load", this.onLoad, this);
23493         ds.un("loadexception", this.onLoadError, this);
23494         ds.un("remove", this.updateInfo, this);
23495         ds.un("add", this.updateInfo, this);
23496         this.ds = undefined;
23497     },
23498
23499     /**
23500      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23501      * @param {Roo.data.Store} store The data store to bind
23502      */
23503     bind : function(ds){
23504         ds.on("beforeload", this.beforeLoad, this);
23505         ds.on("load", this.onLoad, this);
23506         ds.on("loadexception", this.onLoadError, this);
23507         ds.on("remove", this.updateInfo, this);
23508         ds.on("add", this.updateInfo, this);
23509         this.ds = ds;
23510     }
23511 });/*
23512  * - LGPL
23513  *
23514  * element
23515  * 
23516  */
23517
23518 /**
23519  * @class Roo.bootstrap.MessageBar
23520  * @extends Roo.bootstrap.Component
23521  * Bootstrap MessageBar class
23522  * @cfg {String} html contents of the MessageBar
23523  * @cfg {String} weight (info | success | warning | danger) default info
23524  * @cfg {String} beforeClass insert the bar before the given class
23525  * @cfg {Boolean} closable (true | false) default false
23526  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23527  * 
23528  * @constructor
23529  * Create a new Element
23530  * @param {Object} config The config object
23531  */
23532
23533 Roo.bootstrap.MessageBar = function(config){
23534     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23535 };
23536
23537 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23538     
23539     html: '',
23540     weight: 'info',
23541     closable: false,
23542     fixed: false,
23543     beforeClass: 'bootstrap-sticky-wrap',
23544     
23545     getAutoCreate : function(){
23546         
23547         var cfg = {
23548             tag: 'div',
23549             cls: 'alert alert-dismissable alert-' + this.weight,
23550             cn: [
23551                 {
23552                     tag: 'span',
23553                     cls: 'message',
23554                     html: this.html || ''
23555                 }
23556             ]
23557         };
23558         
23559         if(this.fixed){
23560             cfg.cls += ' alert-messages-fixed';
23561         }
23562         
23563         if(this.closable){
23564             cfg.cn.push({
23565                 tag: 'button',
23566                 cls: 'close',
23567                 html: 'x'
23568             });
23569         }
23570         
23571         return cfg;
23572     },
23573     
23574     onRender : function(ct, position)
23575     {
23576         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23577         
23578         if(!this.el){
23579             var cfg = Roo.apply({},  this.getAutoCreate());
23580             cfg.id = Roo.id();
23581             
23582             if (this.cls) {
23583                 cfg.cls += ' ' + this.cls;
23584             }
23585             if (this.style) {
23586                 cfg.style = this.style;
23587             }
23588             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23589             
23590             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23591         }
23592         
23593         this.el.select('>button.close').on('click', this.hide, this);
23594         
23595     },
23596     
23597     show : function()
23598     {
23599         if (!this.rendered) {
23600             this.render();
23601         }
23602         
23603         this.el.show();
23604         
23605         this.fireEvent('show', this);
23606         
23607     },
23608     
23609     hide : function()
23610     {
23611         if (!this.rendered) {
23612             this.render();
23613         }
23614         
23615         this.el.hide();
23616         
23617         this.fireEvent('hide', this);
23618     },
23619     
23620     update : function()
23621     {
23622 //        var e = this.el.dom.firstChild;
23623 //        
23624 //        if(this.closable){
23625 //            e = e.nextSibling;
23626 //        }
23627 //        
23628 //        e.data = this.html || '';
23629
23630         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23631     }
23632    
23633 });
23634
23635  
23636
23637      /*
23638  * - LGPL
23639  *
23640  * Graph
23641  * 
23642  */
23643
23644
23645 /**
23646  * @class Roo.bootstrap.Graph
23647  * @extends Roo.bootstrap.Component
23648  * Bootstrap Graph class
23649 > Prameters
23650  -sm {number} sm 4
23651  -md {number} md 5
23652  @cfg {String} graphtype  bar | vbar | pie
23653  @cfg {number} g_x coodinator | centre x (pie)
23654  @cfg {number} g_y coodinator | centre y (pie)
23655  @cfg {number} g_r radius (pie)
23656  @cfg {number} g_height height of the chart (respected by all elements in the set)
23657  @cfg {number} g_width width of the chart (respected by all elements in the set)
23658  @cfg {Object} title The title of the chart
23659     
23660  -{Array}  values
23661  -opts (object) options for the chart 
23662      o {
23663      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23664      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23665      o vgutter (number)
23666      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.
23667      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23668      o to
23669      o stretch (boolean)
23670      o }
23671  -opts (object) options for the pie
23672      o{
23673      o cut
23674      o startAngle (number)
23675      o endAngle (number)
23676      } 
23677  *
23678  * @constructor
23679  * Create a new Input
23680  * @param {Object} config The config object
23681  */
23682
23683 Roo.bootstrap.Graph = function(config){
23684     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23685     
23686     this.addEvents({
23687         // img events
23688         /**
23689          * @event click
23690          * The img click event for the img.
23691          * @param {Roo.EventObject} e
23692          */
23693         "click" : true
23694     });
23695 };
23696
23697 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23698     
23699     sm: 4,
23700     md: 5,
23701     graphtype: 'bar',
23702     g_height: 250,
23703     g_width: 400,
23704     g_x: 50,
23705     g_y: 50,
23706     g_r: 30,
23707     opts:{
23708         //g_colors: this.colors,
23709         g_type: 'soft',
23710         g_gutter: '20%'
23711
23712     },
23713     title : false,
23714
23715     getAutoCreate : function(){
23716         
23717         var cfg = {
23718             tag: 'div',
23719             html : null
23720         };
23721         
23722         
23723         return  cfg;
23724     },
23725
23726     onRender : function(ct,position){
23727         
23728         
23729         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23730         
23731         if (typeof(Raphael) == 'undefined') {
23732             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23733             return;
23734         }
23735         
23736         this.raphael = Raphael(this.el.dom);
23737         
23738                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23739                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23740                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23741                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23742                 /*
23743                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23744                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23745                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23746                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23747                 
23748                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23749                 r.barchart(330, 10, 300, 220, data1);
23750                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23751                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23752                 */
23753                 
23754                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23755                 // r.barchart(30, 30, 560, 250,  xdata, {
23756                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23757                 //     axis : "0 0 1 1",
23758                 //     axisxlabels :  xdata
23759                 //     //yvalues : cols,
23760                    
23761                 // });
23762 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23763 //        
23764 //        this.load(null,xdata,{
23765 //                axis : "0 0 1 1",
23766 //                axisxlabels :  xdata
23767 //                });
23768
23769     },
23770
23771     load : function(graphtype,xdata,opts)
23772     {
23773         this.raphael.clear();
23774         if(!graphtype) {
23775             graphtype = this.graphtype;
23776         }
23777         if(!opts){
23778             opts = this.opts;
23779         }
23780         var r = this.raphael,
23781             fin = function () {
23782                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23783             },
23784             fout = function () {
23785                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23786             },
23787             pfin = function() {
23788                 this.sector.stop();
23789                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23790
23791                 if (this.label) {
23792                     this.label[0].stop();
23793                     this.label[0].attr({ r: 7.5 });
23794                     this.label[1].attr({ "font-weight": 800 });
23795                 }
23796             },
23797             pfout = function() {
23798                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23799
23800                 if (this.label) {
23801                     this.label[0].animate({ r: 5 }, 500, "bounce");
23802                     this.label[1].attr({ "font-weight": 400 });
23803                 }
23804             };
23805
23806         switch(graphtype){
23807             case 'bar':
23808                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23809                 break;
23810             case 'hbar':
23811                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23812                 break;
23813             case 'pie':
23814 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23815 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23816 //            
23817                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23818                 
23819                 break;
23820
23821         }
23822         
23823         if(this.title){
23824             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23825         }
23826         
23827     },
23828     
23829     setTitle: function(o)
23830     {
23831         this.title = o;
23832     },
23833     
23834     initEvents: function() {
23835         
23836         if(!this.href){
23837             this.el.on('click', this.onClick, this);
23838         }
23839     },
23840     
23841     onClick : function(e)
23842     {
23843         Roo.log('img onclick');
23844         this.fireEvent('click', this, e);
23845     }
23846    
23847 });
23848
23849  
23850 /*
23851  * - LGPL
23852  *
23853  * numberBox
23854  * 
23855  */
23856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23857
23858 /**
23859  * @class Roo.bootstrap.dash.NumberBox
23860  * @extends Roo.bootstrap.Component
23861  * Bootstrap NumberBox class
23862  * @cfg {String} headline Box headline
23863  * @cfg {String} content Box content
23864  * @cfg {String} icon Box icon
23865  * @cfg {String} footer Footer text
23866  * @cfg {String} fhref Footer href
23867  * 
23868  * @constructor
23869  * Create a new NumberBox
23870  * @param {Object} config The config object
23871  */
23872
23873
23874 Roo.bootstrap.dash.NumberBox = function(config){
23875     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23876     
23877 };
23878
23879 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23880     
23881     headline : '',
23882     content : '',
23883     icon : '',
23884     footer : '',
23885     fhref : '',
23886     ficon : '',
23887     
23888     getAutoCreate : function(){
23889         
23890         var cfg = {
23891             tag : 'div',
23892             cls : 'small-box ',
23893             cn : [
23894                 {
23895                     tag : 'div',
23896                     cls : 'inner',
23897                     cn :[
23898                         {
23899                             tag : 'h3',
23900                             cls : 'roo-headline',
23901                             html : this.headline
23902                         },
23903                         {
23904                             tag : 'p',
23905                             cls : 'roo-content',
23906                             html : this.content
23907                         }
23908                     ]
23909                 }
23910             ]
23911         };
23912         
23913         if(this.icon){
23914             cfg.cn.push({
23915                 tag : 'div',
23916                 cls : 'icon',
23917                 cn :[
23918                     {
23919                         tag : 'i',
23920                         cls : 'ion ' + this.icon
23921                     }
23922                 ]
23923             });
23924         }
23925         
23926         if(this.footer){
23927             var footer = {
23928                 tag : 'a',
23929                 cls : 'small-box-footer',
23930                 href : this.fhref || '#',
23931                 html : this.footer
23932             };
23933             
23934             cfg.cn.push(footer);
23935             
23936         }
23937         
23938         return  cfg;
23939     },
23940
23941     onRender : function(ct,position){
23942         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23943
23944
23945        
23946                 
23947     },
23948
23949     setHeadline: function (value)
23950     {
23951         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23952     },
23953     
23954     setFooter: function (value, href)
23955     {
23956         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23957         
23958         if(href){
23959             this.el.select('a.small-box-footer',true).first().attr('href', href);
23960         }
23961         
23962     },
23963
23964     setContent: function (value)
23965     {
23966         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23967     },
23968
23969     initEvents: function() 
23970     {   
23971         
23972     }
23973     
23974 });
23975
23976  
23977 /*
23978  * - LGPL
23979  *
23980  * TabBox
23981  * 
23982  */
23983 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23984
23985 /**
23986  * @class Roo.bootstrap.dash.TabBox
23987  * @extends Roo.bootstrap.Component
23988  * Bootstrap TabBox class
23989  * @cfg {String} title Title of the TabBox
23990  * @cfg {String} icon Icon of the TabBox
23991  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23992  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23993  * 
23994  * @constructor
23995  * Create a new TabBox
23996  * @param {Object} config The config object
23997  */
23998
23999
24000 Roo.bootstrap.dash.TabBox = function(config){
24001     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24002     this.addEvents({
24003         // raw events
24004         /**
24005          * @event addpane
24006          * When a pane is added
24007          * @param {Roo.bootstrap.dash.TabPane} pane
24008          */
24009         "addpane" : true,
24010         /**
24011          * @event activatepane
24012          * When a pane is activated
24013          * @param {Roo.bootstrap.dash.TabPane} pane
24014          */
24015         "activatepane" : true
24016         
24017          
24018     });
24019     
24020     this.panes = [];
24021 };
24022
24023 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24024
24025     title : '',
24026     icon : false,
24027     showtabs : true,
24028     tabScrollable : false,
24029     
24030     getChildContainer : function()
24031     {
24032         return this.el.select('.tab-content', true).first();
24033     },
24034     
24035     getAutoCreate : function(){
24036         
24037         var header = {
24038             tag: 'li',
24039             cls: 'pull-left header',
24040             html: this.title,
24041             cn : []
24042         };
24043         
24044         if(this.icon){
24045             header.cn.push({
24046                 tag: 'i',
24047                 cls: 'fa ' + this.icon
24048             });
24049         }
24050         
24051         var h = {
24052             tag: 'ul',
24053             cls: 'nav nav-tabs pull-right',
24054             cn: [
24055                 header
24056             ]
24057         };
24058         
24059         if(this.tabScrollable){
24060             h = {
24061                 tag: 'div',
24062                 cls: 'tab-header',
24063                 cn: [
24064                     {
24065                         tag: 'ul',
24066                         cls: 'nav nav-tabs pull-right',
24067                         cn: [
24068                             header
24069                         ]
24070                     }
24071                 ]
24072             };
24073         }
24074         
24075         var cfg = {
24076             tag: 'div',
24077             cls: 'nav-tabs-custom',
24078             cn: [
24079                 h,
24080                 {
24081                     tag: 'div',
24082                     cls: 'tab-content no-padding',
24083                     cn: []
24084                 }
24085             ]
24086         };
24087
24088         return  cfg;
24089     },
24090     initEvents : function()
24091     {
24092         //Roo.log('add add pane handler');
24093         this.on('addpane', this.onAddPane, this);
24094     },
24095      /**
24096      * Updates the box title
24097      * @param {String} html to set the title to.
24098      */
24099     setTitle : function(value)
24100     {
24101         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24102     },
24103     onAddPane : function(pane)
24104     {
24105         this.panes.push(pane);
24106         //Roo.log('addpane');
24107         //Roo.log(pane);
24108         // tabs are rendere left to right..
24109         if(!this.showtabs){
24110             return;
24111         }
24112         
24113         var ctr = this.el.select('.nav-tabs', true).first();
24114          
24115          
24116         var existing = ctr.select('.nav-tab',true);
24117         var qty = existing.getCount();;
24118         
24119         
24120         var tab = ctr.createChild({
24121             tag : 'li',
24122             cls : 'nav-tab' + (qty ? '' : ' active'),
24123             cn : [
24124                 {
24125                     tag : 'a',
24126                     href:'#',
24127                     html : pane.title
24128                 }
24129             ]
24130         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24131         pane.tab = tab;
24132         
24133         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24134         if (!qty) {
24135             pane.el.addClass('active');
24136         }
24137         
24138                 
24139     },
24140     onTabClick : function(ev,un,ob,pane)
24141     {
24142         //Roo.log('tab - prev default');
24143         ev.preventDefault();
24144         
24145         
24146         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24147         pane.tab.addClass('active');
24148         //Roo.log(pane.title);
24149         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24150         // technically we should have a deactivate event.. but maybe add later.
24151         // and it should not de-activate the selected tab...
24152         this.fireEvent('activatepane', pane);
24153         pane.el.addClass('active');
24154         pane.fireEvent('activate');
24155         
24156         
24157     },
24158     
24159     getActivePane : function()
24160     {
24161         var r = false;
24162         Roo.each(this.panes, function(p) {
24163             if(p.el.hasClass('active')){
24164                 r = p;
24165                 return false;
24166             }
24167             
24168             return;
24169         });
24170         
24171         return r;
24172     }
24173     
24174     
24175 });
24176
24177  
24178 /*
24179  * - LGPL
24180  *
24181  * Tab pane
24182  * 
24183  */
24184 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24185 /**
24186  * @class Roo.bootstrap.TabPane
24187  * @extends Roo.bootstrap.Component
24188  * Bootstrap TabPane class
24189  * @cfg {Boolean} active (false | true) Default false
24190  * @cfg {String} title title of panel
24191
24192  * 
24193  * @constructor
24194  * Create a new TabPane
24195  * @param {Object} config The config object
24196  */
24197
24198 Roo.bootstrap.dash.TabPane = function(config){
24199     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24200     
24201     this.addEvents({
24202         // raw events
24203         /**
24204          * @event activate
24205          * When a pane is activated
24206          * @param {Roo.bootstrap.dash.TabPane} pane
24207          */
24208         "activate" : true
24209          
24210     });
24211 };
24212
24213 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24214     
24215     active : false,
24216     title : '',
24217     
24218     // the tabBox that this is attached to.
24219     tab : false,
24220      
24221     getAutoCreate : function() 
24222     {
24223         var cfg = {
24224             tag: 'div',
24225             cls: 'tab-pane'
24226         };
24227         
24228         if(this.active){
24229             cfg.cls += ' active';
24230         }
24231         
24232         return cfg;
24233     },
24234     initEvents  : function()
24235     {
24236         //Roo.log('trigger add pane handler');
24237         this.parent().fireEvent('addpane', this)
24238     },
24239     
24240      /**
24241      * Updates the tab title 
24242      * @param {String} html to set the title to.
24243      */
24244     setTitle: function(str)
24245     {
24246         if (!this.tab) {
24247             return;
24248         }
24249         this.title = str;
24250         this.tab.select('a', true).first().dom.innerHTML = str;
24251         
24252     }
24253     
24254     
24255     
24256 });
24257
24258  
24259
24260
24261  /*
24262  * - LGPL
24263  *
24264  * menu
24265  * 
24266  */
24267 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24268
24269 /**
24270  * @class Roo.bootstrap.menu.Menu
24271  * @extends Roo.bootstrap.Component
24272  * Bootstrap Menu class - container for Menu
24273  * @cfg {String} html Text of the menu
24274  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24275  * @cfg {String} icon Font awesome icon
24276  * @cfg {String} pos Menu align to (top | bottom) default bottom
24277  * 
24278  * 
24279  * @constructor
24280  * Create a new Menu
24281  * @param {Object} config The config object
24282  */
24283
24284
24285 Roo.bootstrap.menu.Menu = function(config){
24286     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24287     
24288     this.addEvents({
24289         /**
24290          * @event beforeshow
24291          * Fires before this menu is displayed
24292          * @param {Roo.bootstrap.menu.Menu} this
24293          */
24294         beforeshow : true,
24295         /**
24296          * @event beforehide
24297          * Fires before this menu is hidden
24298          * @param {Roo.bootstrap.menu.Menu} this
24299          */
24300         beforehide : true,
24301         /**
24302          * @event show
24303          * Fires after this menu is displayed
24304          * @param {Roo.bootstrap.menu.Menu} this
24305          */
24306         show : true,
24307         /**
24308          * @event hide
24309          * Fires after this menu is hidden
24310          * @param {Roo.bootstrap.menu.Menu} this
24311          */
24312         hide : true,
24313         /**
24314          * @event click
24315          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24316          * @param {Roo.bootstrap.menu.Menu} this
24317          * @param {Roo.EventObject} e
24318          */
24319         click : true
24320     });
24321     
24322 };
24323
24324 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24325     
24326     submenu : false,
24327     html : '',
24328     weight : 'default',
24329     icon : false,
24330     pos : 'bottom',
24331     
24332     
24333     getChildContainer : function() {
24334         if(this.isSubMenu){
24335             return this.el;
24336         }
24337         
24338         return this.el.select('ul.dropdown-menu', true).first();  
24339     },
24340     
24341     getAutoCreate : function()
24342     {
24343         var text = [
24344             {
24345                 tag : 'span',
24346                 cls : 'roo-menu-text',
24347                 html : this.html
24348             }
24349         ];
24350         
24351         if(this.icon){
24352             text.unshift({
24353                 tag : 'i',
24354                 cls : 'fa ' + this.icon
24355             })
24356         }
24357         
24358         
24359         var cfg = {
24360             tag : 'div',
24361             cls : 'btn-group',
24362             cn : [
24363                 {
24364                     tag : 'button',
24365                     cls : 'dropdown-button btn btn-' + this.weight,
24366                     cn : text
24367                 },
24368                 {
24369                     tag : 'button',
24370                     cls : 'dropdown-toggle btn btn-' + this.weight,
24371                     cn : [
24372                         {
24373                             tag : 'span',
24374                             cls : 'caret'
24375                         }
24376                     ]
24377                 },
24378                 {
24379                     tag : 'ul',
24380                     cls : 'dropdown-menu'
24381                 }
24382             ]
24383             
24384         };
24385         
24386         if(this.pos == 'top'){
24387             cfg.cls += ' dropup';
24388         }
24389         
24390         if(this.isSubMenu){
24391             cfg = {
24392                 tag : 'ul',
24393                 cls : 'dropdown-menu'
24394             }
24395         }
24396         
24397         return cfg;
24398     },
24399     
24400     onRender : function(ct, position)
24401     {
24402         this.isSubMenu = ct.hasClass('dropdown-submenu');
24403         
24404         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24405     },
24406     
24407     initEvents : function() 
24408     {
24409         if(this.isSubMenu){
24410             return;
24411         }
24412         
24413         this.hidden = true;
24414         
24415         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24416         this.triggerEl.on('click', this.onTriggerPress, this);
24417         
24418         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24419         this.buttonEl.on('click', this.onClick, this);
24420         
24421     },
24422     
24423     list : function()
24424     {
24425         if(this.isSubMenu){
24426             return this.el;
24427         }
24428         
24429         return this.el.select('ul.dropdown-menu', true).first();
24430     },
24431     
24432     onClick : function(e)
24433     {
24434         this.fireEvent("click", this, e);
24435     },
24436     
24437     onTriggerPress  : function(e)
24438     {   
24439         if (this.isVisible()) {
24440             this.hide();
24441         } else {
24442             this.show();
24443         }
24444     },
24445     
24446     isVisible : function(){
24447         return !this.hidden;
24448     },
24449     
24450     show : function()
24451     {
24452         this.fireEvent("beforeshow", this);
24453         
24454         this.hidden = false;
24455         this.el.addClass('open');
24456         
24457         Roo.get(document).on("mouseup", this.onMouseUp, this);
24458         
24459         this.fireEvent("show", this);
24460         
24461         
24462     },
24463     
24464     hide : function()
24465     {
24466         this.fireEvent("beforehide", this);
24467         
24468         this.hidden = true;
24469         this.el.removeClass('open');
24470         
24471         Roo.get(document).un("mouseup", this.onMouseUp);
24472         
24473         this.fireEvent("hide", this);
24474     },
24475     
24476     onMouseUp : function()
24477     {
24478         this.hide();
24479     }
24480     
24481 });
24482
24483  
24484  /*
24485  * - LGPL
24486  *
24487  * menu item
24488  * 
24489  */
24490 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24491
24492 /**
24493  * @class Roo.bootstrap.menu.Item
24494  * @extends Roo.bootstrap.Component
24495  * Bootstrap MenuItem class
24496  * @cfg {Boolean} submenu (true | false) default false
24497  * @cfg {String} html text of the item
24498  * @cfg {String} href the link
24499  * @cfg {Boolean} disable (true | false) default false
24500  * @cfg {Boolean} preventDefault (true | false) default true
24501  * @cfg {String} icon Font awesome icon
24502  * @cfg {String} pos Submenu align to (left | right) default right 
24503  * 
24504  * 
24505  * @constructor
24506  * Create a new Item
24507  * @param {Object} config The config object
24508  */
24509
24510
24511 Roo.bootstrap.menu.Item = function(config){
24512     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24513     this.addEvents({
24514         /**
24515          * @event mouseover
24516          * Fires when the mouse is hovering over this menu
24517          * @param {Roo.bootstrap.menu.Item} this
24518          * @param {Roo.EventObject} e
24519          */
24520         mouseover : true,
24521         /**
24522          * @event mouseout
24523          * Fires when the mouse exits this menu
24524          * @param {Roo.bootstrap.menu.Item} this
24525          * @param {Roo.EventObject} e
24526          */
24527         mouseout : true,
24528         // raw events
24529         /**
24530          * @event click
24531          * The raw click event for the entire grid.
24532          * @param {Roo.EventObject} e
24533          */
24534         click : true
24535     });
24536 };
24537
24538 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24539     
24540     submenu : false,
24541     href : '',
24542     html : '',
24543     preventDefault: true,
24544     disable : false,
24545     icon : false,
24546     pos : 'right',
24547     
24548     getAutoCreate : function()
24549     {
24550         var text = [
24551             {
24552                 tag : 'span',
24553                 cls : 'roo-menu-item-text',
24554                 html : this.html
24555             }
24556         ];
24557         
24558         if(this.icon){
24559             text.unshift({
24560                 tag : 'i',
24561                 cls : 'fa ' + this.icon
24562             })
24563         }
24564         
24565         var cfg = {
24566             tag : 'li',
24567             cn : [
24568                 {
24569                     tag : 'a',
24570                     href : this.href || '#',
24571                     cn : text
24572                 }
24573             ]
24574         };
24575         
24576         if(this.disable){
24577             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24578         }
24579         
24580         if(this.submenu){
24581             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24582             
24583             if(this.pos == 'left'){
24584                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24585             }
24586         }
24587         
24588         return cfg;
24589     },
24590     
24591     initEvents : function() 
24592     {
24593         this.el.on('mouseover', this.onMouseOver, this);
24594         this.el.on('mouseout', this.onMouseOut, this);
24595         
24596         this.el.select('a', true).first().on('click', this.onClick, this);
24597         
24598     },
24599     
24600     onClick : function(e)
24601     {
24602         if(this.preventDefault){
24603             e.preventDefault();
24604         }
24605         
24606         this.fireEvent("click", this, e);
24607     },
24608     
24609     onMouseOver : function(e)
24610     {
24611         if(this.submenu && this.pos == 'left'){
24612             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24613         }
24614         
24615         this.fireEvent("mouseover", this, e);
24616     },
24617     
24618     onMouseOut : function(e)
24619     {
24620         this.fireEvent("mouseout", this, e);
24621     }
24622 });
24623
24624  
24625
24626  /*
24627  * - LGPL
24628  *
24629  * menu separator
24630  * 
24631  */
24632 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24633
24634 /**
24635  * @class Roo.bootstrap.menu.Separator
24636  * @extends Roo.bootstrap.Component
24637  * Bootstrap Separator class
24638  * 
24639  * @constructor
24640  * Create a new Separator
24641  * @param {Object} config The config object
24642  */
24643
24644
24645 Roo.bootstrap.menu.Separator = function(config){
24646     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24647 };
24648
24649 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24650     
24651     getAutoCreate : function(){
24652         var cfg = {
24653             tag : 'li',
24654             cls: 'divider'
24655         };
24656         
24657         return cfg;
24658     }
24659    
24660 });
24661
24662  
24663
24664  /*
24665  * - LGPL
24666  *
24667  * Tooltip
24668  * 
24669  */
24670
24671 /**
24672  * @class Roo.bootstrap.Tooltip
24673  * Bootstrap Tooltip class
24674  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24675  * to determine which dom element triggers the tooltip.
24676  * 
24677  * It needs to add support for additional attributes like tooltip-position
24678  * 
24679  * @constructor
24680  * Create a new Toolti
24681  * @param {Object} config The config object
24682  */
24683
24684 Roo.bootstrap.Tooltip = function(config){
24685     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24686 };
24687
24688 Roo.apply(Roo.bootstrap.Tooltip, {
24689     /**
24690      * @function init initialize tooltip monitoring.
24691      * @static
24692      */
24693     currentEl : false,
24694     currentTip : false,
24695     currentRegion : false,
24696     
24697     //  init : delay?
24698     
24699     init : function()
24700     {
24701         Roo.get(document).on('mouseover', this.enter ,this);
24702         Roo.get(document).on('mouseout', this.leave, this);
24703          
24704         
24705         this.currentTip = new Roo.bootstrap.Tooltip();
24706     },
24707     
24708     enter : function(ev)
24709     {
24710         var dom = ev.getTarget();
24711         
24712         //Roo.log(['enter',dom]);
24713         var el = Roo.fly(dom);
24714         if (this.currentEl) {
24715             //Roo.log(dom);
24716             //Roo.log(this.currentEl);
24717             //Roo.log(this.currentEl.contains(dom));
24718             if (this.currentEl == el) {
24719                 return;
24720             }
24721             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24722                 return;
24723             }
24724
24725         }
24726         
24727         if (this.currentTip.el) {
24728             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24729         }    
24730         //Roo.log(ev);
24731         
24732         if(!el || el.dom == document){
24733             return;
24734         }
24735         
24736         var bindEl = el;
24737         
24738         // you can not look for children, as if el is the body.. then everythign is the child..
24739         if (!el.attr('tooltip')) { //
24740             if (!el.select("[tooltip]").elements.length) {
24741                 return;
24742             }
24743             // is the mouse over this child...?
24744             bindEl = el.select("[tooltip]").first();
24745             var xy = ev.getXY();
24746             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24747                 //Roo.log("not in region.");
24748                 return;
24749             }
24750             //Roo.log("child element over..");
24751             
24752         }
24753         this.currentEl = bindEl;
24754         this.currentTip.bind(bindEl);
24755         this.currentRegion = Roo.lib.Region.getRegion(dom);
24756         this.currentTip.enter();
24757         
24758     },
24759     leave : function(ev)
24760     {
24761         var dom = ev.getTarget();
24762         //Roo.log(['leave',dom]);
24763         if (!this.currentEl) {
24764             return;
24765         }
24766         
24767         
24768         if (dom != this.currentEl.dom) {
24769             return;
24770         }
24771         var xy = ev.getXY();
24772         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24773             return;
24774         }
24775         // only activate leave if mouse cursor is outside... bounding box..
24776         
24777         
24778         
24779         
24780         if (this.currentTip) {
24781             this.currentTip.leave();
24782         }
24783         //Roo.log('clear currentEl');
24784         this.currentEl = false;
24785         
24786         
24787     },
24788     alignment : {
24789         'left' : ['r-l', [-2,0], 'right'],
24790         'right' : ['l-r', [2,0], 'left'],
24791         'bottom' : ['t-b', [0,2], 'top'],
24792         'top' : [ 'b-t', [0,-2], 'bottom']
24793     }
24794     
24795 });
24796
24797
24798 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24799     
24800     
24801     bindEl : false,
24802     
24803     delay : null, // can be { show : 300 , hide: 500}
24804     
24805     timeout : null,
24806     
24807     hoverState : null, //???
24808     
24809     placement : 'bottom', 
24810     
24811     getAutoCreate : function(){
24812     
24813         var cfg = {
24814            cls : 'tooltip',
24815            role : 'tooltip',
24816            cn : [
24817                 {
24818                     cls : 'tooltip-arrow'
24819                 },
24820                 {
24821                     cls : 'tooltip-inner'
24822                 }
24823            ]
24824         };
24825         
24826         return cfg;
24827     },
24828     bind : function(el)
24829     {
24830         this.bindEl = el;
24831     },
24832       
24833     
24834     enter : function () {
24835        
24836         if (this.timeout != null) {
24837             clearTimeout(this.timeout);
24838         }
24839         
24840         this.hoverState = 'in';
24841          //Roo.log("enter - show");
24842         if (!this.delay || !this.delay.show) {
24843             this.show();
24844             return;
24845         }
24846         var _t = this;
24847         this.timeout = setTimeout(function () {
24848             if (_t.hoverState == 'in') {
24849                 _t.show();
24850             }
24851         }, this.delay.show);
24852     },
24853     leave : function()
24854     {
24855         clearTimeout(this.timeout);
24856     
24857         this.hoverState = 'out';
24858          if (!this.delay || !this.delay.hide) {
24859             this.hide();
24860             return;
24861         }
24862        
24863         var _t = this;
24864         this.timeout = setTimeout(function () {
24865             //Roo.log("leave - timeout");
24866             
24867             if (_t.hoverState == 'out') {
24868                 _t.hide();
24869                 Roo.bootstrap.Tooltip.currentEl = false;
24870             }
24871         }, delay);
24872     },
24873     
24874     show : function ()
24875     {
24876         if (!this.el) {
24877             this.render(document.body);
24878         }
24879         // set content.
24880         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24881         
24882         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24883         
24884         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24885         
24886         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24887         
24888         var placement = typeof this.placement == 'function' ?
24889             this.placement.call(this, this.el, on_el) :
24890             this.placement;
24891             
24892         var autoToken = /\s?auto?\s?/i;
24893         var autoPlace = autoToken.test(placement);
24894         if (autoPlace) {
24895             placement = placement.replace(autoToken, '') || 'top';
24896         }
24897         
24898         //this.el.detach()
24899         //this.el.setXY([0,0]);
24900         this.el.show();
24901         //this.el.dom.style.display='block';
24902         
24903         //this.el.appendTo(on_el);
24904         
24905         var p = this.getPosition();
24906         var box = this.el.getBox();
24907         
24908         if (autoPlace) {
24909             // fixme..
24910         }
24911         
24912         var align = Roo.bootstrap.Tooltip.alignment[placement];
24913         
24914         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24915         
24916         if(placement == 'top' || placement == 'bottom'){
24917             if(xy[0] < 0){
24918                 placement = 'right';
24919             }
24920             
24921             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24922                 placement = 'left';
24923             }
24924             
24925             var scroll = Roo.select('body', true).first().getScroll();
24926             
24927             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24928                 placement = 'top';
24929             }
24930             
24931         }
24932         
24933         align = Roo.bootstrap.Tooltip.alignment[placement];
24934         
24935         this.el.alignTo(this.bindEl, align[0],align[1]);
24936         //var arrow = this.el.select('.arrow',true).first();
24937         //arrow.set(align[2], 
24938         
24939         this.el.addClass(placement);
24940         
24941         this.el.addClass('in fade');
24942         
24943         this.hoverState = null;
24944         
24945         if (this.el.hasClass('fade')) {
24946             // fade it?
24947         }
24948         
24949     },
24950     hide : function()
24951     {
24952          
24953         if (!this.el) {
24954             return;
24955         }
24956         //this.el.setXY([0,0]);
24957         this.el.removeClass('in');
24958         //this.el.hide();
24959         
24960     }
24961     
24962 });
24963  
24964
24965  /*
24966  * - LGPL
24967  *
24968  * Location Picker
24969  * 
24970  */
24971
24972 /**
24973  * @class Roo.bootstrap.LocationPicker
24974  * @extends Roo.bootstrap.Component
24975  * Bootstrap LocationPicker class
24976  * @cfg {Number} latitude Position when init default 0
24977  * @cfg {Number} longitude Position when init default 0
24978  * @cfg {Number} zoom default 15
24979  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24980  * @cfg {Boolean} mapTypeControl default false
24981  * @cfg {Boolean} disableDoubleClickZoom default false
24982  * @cfg {Boolean} scrollwheel default true
24983  * @cfg {Boolean} streetViewControl default false
24984  * @cfg {Number} radius default 0
24985  * @cfg {String} locationName
24986  * @cfg {Boolean} draggable default true
24987  * @cfg {Boolean} enableAutocomplete default false
24988  * @cfg {Boolean} enableReverseGeocode default true
24989  * @cfg {String} markerTitle
24990  * 
24991  * @constructor
24992  * Create a new LocationPicker
24993  * @param {Object} config The config object
24994  */
24995
24996
24997 Roo.bootstrap.LocationPicker = function(config){
24998     
24999     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25000     
25001     this.addEvents({
25002         /**
25003          * @event initial
25004          * Fires when the picker initialized.
25005          * @param {Roo.bootstrap.LocationPicker} this
25006          * @param {Google Location} location
25007          */
25008         initial : true,
25009         /**
25010          * @event positionchanged
25011          * Fires when the picker position changed.
25012          * @param {Roo.bootstrap.LocationPicker} this
25013          * @param {Google Location} location
25014          */
25015         positionchanged : true,
25016         /**
25017          * @event resize
25018          * Fires when the map resize.
25019          * @param {Roo.bootstrap.LocationPicker} this
25020          */
25021         resize : true,
25022         /**
25023          * @event show
25024          * Fires when the map show.
25025          * @param {Roo.bootstrap.LocationPicker} this
25026          */
25027         show : true,
25028         /**
25029          * @event hide
25030          * Fires when the map hide.
25031          * @param {Roo.bootstrap.LocationPicker} this
25032          */
25033         hide : true,
25034         /**
25035          * @event mapClick
25036          * Fires when click the map.
25037          * @param {Roo.bootstrap.LocationPicker} this
25038          * @param {Map event} e
25039          */
25040         mapClick : true,
25041         /**
25042          * @event mapRightClick
25043          * Fires when right click the map.
25044          * @param {Roo.bootstrap.LocationPicker} this
25045          * @param {Map event} e
25046          */
25047         mapRightClick : true,
25048         /**
25049          * @event markerClick
25050          * Fires when click the marker.
25051          * @param {Roo.bootstrap.LocationPicker} this
25052          * @param {Map event} e
25053          */
25054         markerClick : true,
25055         /**
25056          * @event markerRightClick
25057          * Fires when right click the marker.
25058          * @param {Roo.bootstrap.LocationPicker} this
25059          * @param {Map event} e
25060          */
25061         markerRightClick : true,
25062         /**
25063          * @event OverlayViewDraw
25064          * Fires when OverlayView Draw
25065          * @param {Roo.bootstrap.LocationPicker} this
25066          */
25067         OverlayViewDraw : true,
25068         /**
25069          * @event OverlayViewOnAdd
25070          * Fires when OverlayView Draw
25071          * @param {Roo.bootstrap.LocationPicker} this
25072          */
25073         OverlayViewOnAdd : true,
25074         /**
25075          * @event OverlayViewOnRemove
25076          * Fires when OverlayView Draw
25077          * @param {Roo.bootstrap.LocationPicker} this
25078          */
25079         OverlayViewOnRemove : true,
25080         /**
25081          * @event OverlayViewShow
25082          * Fires when OverlayView Draw
25083          * @param {Roo.bootstrap.LocationPicker} this
25084          * @param {Pixel} cpx
25085          */
25086         OverlayViewShow : true,
25087         /**
25088          * @event OverlayViewHide
25089          * Fires when OverlayView Draw
25090          * @param {Roo.bootstrap.LocationPicker} this
25091          */
25092         OverlayViewHide : true,
25093         /**
25094          * @event loadexception
25095          * Fires when load google lib failed.
25096          * @param {Roo.bootstrap.LocationPicker} this
25097          */
25098         loadexception : true
25099     });
25100         
25101 };
25102
25103 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25104     
25105     gMapContext: false,
25106     
25107     latitude: 0,
25108     longitude: 0,
25109     zoom: 15,
25110     mapTypeId: false,
25111     mapTypeControl: false,
25112     disableDoubleClickZoom: false,
25113     scrollwheel: true,
25114     streetViewControl: false,
25115     radius: 0,
25116     locationName: '',
25117     draggable: true,
25118     enableAutocomplete: false,
25119     enableReverseGeocode: true,
25120     markerTitle: '',
25121     
25122     getAutoCreate: function()
25123     {
25124
25125         var cfg = {
25126             tag: 'div',
25127             cls: 'roo-location-picker'
25128         };
25129         
25130         return cfg
25131     },
25132     
25133     initEvents: function(ct, position)
25134     {       
25135         if(!this.el.getWidth() || this.isApplied()){
25136             return;
25137         }
25138         
25139         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25140         
25141         this.initial();
25142     },
25143     
25144     initial: function()
25145     {
25146         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25147             this.fireEvent('loadexception', this);
25148             return;
25149         }
25150         
25151         if(!this.mapTypeId){
25152             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25153         }
25154         
25155         this.gMapContext = this.GMapContext();
25156         
25157         this.initOverlayView();
25158         
25159         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25160         
25161         var _this = this;
25162                 
25163         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25164             _this.setPosition(_this.gMapContext.marker.position);
25165         });
25166         
25167         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25168             _this.fireEvent('mapClick', this, event);
25169             
25170         });
25171
25172         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25173             _this.fireEvent('mapRightClick', this, event);
25174             
25175         });
25176         
25177         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25178             _this.fireEvent('markerClick', this, event);
25179             
25180         });
25181
25182         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25183             _this.fireEvent('markerRightClick', this, event);
25184             
25185         });
25186         
25187         this.setPosition(this.gMapContext.location);
25188         
25189         this.fireEvent('initial', this, this.gMapContext.location);
25190     },
25191     
25192     initOverlayView: function()
25193     {
25194         var _this = this;
25195         
25196         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25197             
25198             draw: function()
25199             {
25200                 _this.fireEvent('OverlayViewDraw', _this);
25201             },
25202             
25203             onAdd: function()
25204             {
25205                 _this.fireEvent('OverlayViewOnAdd', _this);
25206             },
25207             
25208             onRemove: function()
25209             {
25210                 _this.fireEvent('OverlayViewOnRemove', _this);
25211             },
25212             
25213             show: function(cpx)
25214             {
25215                 _this.fireEvent('OverlayViewShow', _this, cpx);
25216             },
25217             
25218             hide: function()
25219             {
25220                 _this.fireEvent('OverlayViewHide', _this);
25221             }
25222             
25223         });
25224     },
25225     
25226     fromLatLngToContainerPixel: function(event)
25227     {
25228         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25229     },
25230     
25231     isApplied: function() 
25232     {
25233         return this.getGmapContext() == false ? false : true;
25234     },
25235     
25236     getGmapContext: function() 
25237     {
25238         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25239     },
25240     
25241     GMapContext: function() 
25242     {
25243         var position = new google.maps.LatLng(this.latitude, this.longitude);
25244         
25245         var _map = new google.maps.Map(this.el.dom, {
25246             center: position,
25247             zoom: this.zoom,
25248             mapTypeId: this.mapTypeId,
25249             mapTypeControl: this.mapTypeControl,
25250             disableDoubleClickZoom: this.disableDoubleClickZoom,
25251             scrollwheel: this.scrollwheel,
25252             streetViewControl: this.streetViewControl,
25253             locationName: this.locationName,
25254             draggable: this.draggable,
25255             enableAutocomplete: this.enableAutocomplete,
25256             enableReverseGeocode: this.enableReverseGeocode
25257         });
25258         
25259         var _marker = new google.maps.Marker({
25260             position: position,
25261             map: _map,
25262             title: this.markerTitle,
25263             draggable: this.draggable
25264         });
25265         
25266         return {
25267             map: _map,
25268             marker: _marker,
25269             circle: null,
25270             location: position,
25271             radius: this.radius,
25272             locationName: this.locationName,
25273             addressComponents: {
25274                 formatted_address: null,
25275                 addressLine1: null,
25276                 addressLine2: null,
25277                 streetName: null,
25278                 streetNumber: null,
25279                 city: null,
25280                 district: null,
25281                 state: null,
25282                 stateOrProvince: null
25283             },
25284             settings: this,
25285             domContainer: this.el.dom,
25286             geodecoder: new google.maps.Geocoder()
25287         };
25288     },
25289     
25290     drawCircle: function(center, radius, options) 
25291     {
25292         if (this.gMapContext.circle != null) {
25293             this.gMapContext.circle.setMap(null);
25294         }
25295         if (radius > 0) {
25296             radius *= 1;
25297             options = Roo.apply({}, options, {
25298                 strokeColor: "#0000FF",
25299                 strokeOpacity: .35,
25300                 strokeWeight: 2,
25301                 fillColor: "#0000FF",
25302                 fillOpacity: .2
25303             });
25304             
25305             options.map = this.gMapContext.map;
25306             options.radius = radius;
25307             options.center = center;
25308             this.gMapContext.circle = new google.maps.Circle(options);
25309             return this.gMapContext.circle;
25310         }
25311         
25312         return null;
25313     },
25314     
25315     setPosition: function(location) 
25316     {
25317         this.gMapContext.location = location;
25318         this.gMapContext.marker.setPosition(location);
25319         this.gMapContext.map.panTo(location);
25320         this.drawCircle(location, this.gMapContext.radius, {});
25321         
25322         var _this = this;
25323         
25324         if (this.gMapContext.settings.enableReverseGeocode) {
25325             this.gMapContext.geodecoder.geocode({
25326                 latLng: this.gMapContext.location
25327             }, function(results, status) {
25328                 
25329                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25330                     _this.gMapContext.locationName = results[0].formatted_address;
25331                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25332                     
25333                     _this.fireEvent('positionchanged', this, location);
25334                 }
25335             });
25336             
25337             return;
25338         }
25339         
25340         this.fireEvent('positionchanged', this, location);
25341     },
25342     
25343     resize: function()
25344     {
25345         google.maps.event.trigger(this.gMapContext.map, "resize");
25346         
25347         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25348         
25349         this.fireEvent('resize', this);
25350     },
25351     
25352     setPositionByLatLng: function(latitude, longitude)
25353     {
25354         this.setPosition(new google.maps.LatLng(latitude, longitude));
25355     },
25356     
25357     getCurrentPosition: function() 
25358     {
25359         return {
25360             latitude: this.gMapContext.location.lat(),
25361             longitude: this.gMapContext.location.lng()
25362         };
25363     },
25364     
25365     getAddressName: function() 
25366     {
25367         return this.gMapContext.locationName;
25368     },
25369     
25370     getAddressComponents: function() 
25371     {
25372         return this.gMapContext.addressComponents;
25373     },
25374     
25375     address_component_from_google_geocode: function(address_components) 
25376     {
25377         var result = {};
25378         
25379         for (var i = 0; i < address_components.length; i++) {
25380             var component = address_components[i];
25381             if (component.types.indexOf("postal_code") >= 0) {
25382                 result.postalCode = component.short_name;
25383             } else if (component.types.indexOf("street_number") >= 0) {
25384                 result.streetNumber = component.short_name;
25385             } else if (component.types.indexOf("route") >= 0) {
25386                 result.streetName = component.short_name;
25387             } else if (component.types.indexOf("neighborhood") >= 0) {
25388                 result.city = component.short_name;
25389             } else if (component.types.indexOf("locality") >= 0) {
25390                 result.city = component.short_name;
25391             } else if (component.types.indexOf("sublocality") >= 0) {
25392                 result.district = component.short_name;
25393             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25394                 result.stateOrProvince = component.short_name;
25395             } else if (component.types.indexOf("country") >= 0) {
25396                 result.country = component.short_name;
25397             }
25398         }
25399         
25400         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25401         result.addressLine2 = "";
25402         return result;
25403     },
25404     
25405     setZoomLevel: function(zoom)
25406     {
25407         this.gMapContext.map.setZoom(zoom);
25408     },
25409     
25410     show: function()
25411     {
25412         if(!this.el){
25413             return;
25414         }
25415         
25416         this.el.show();
25417         
25418         this.resize();
25419         
25420         this.fireEvent('show', this);
25421     },
25422     
25423     hide: function()
25424     {
25425         if(!this.el){
25426             return;
25427         }
25428         
25429         this.el.hide();
25430         
25431         this.fireEvent('hide', this);
25432     }
25433     
25434 });
25435
25436 Roo.apply(Roo.bootstrap.LocationPicker, {
25437     
25438     OverlayView : function(map, options)
25439     {
25440         options = options || {};
25441         
25442         this.setMap(map);
25443     }
25444     
25445     
25446 });/*
25447  * - LGPL
25448  *
25449  * Alert
25450  * 
25451  */
25452
25453 /**
25454  * @class Roo.bootstrap.Alert
25455  * @extends Roo.bootstrap.Component
25456  * Bootstrap Alert class
25457  * @cfg {String} title The title of alert
25458  * @cfg {String} html The content of alert
25459  * @cfg {String} weight (  success | info | warning | danger )
25460  * @cfg {String} faicon font-awesomeicon
25461  * 
25462  * @constructor
25463  * Create a new alert
25464  * @param {Object} config The config object
25465  */
25466
25467
25468 Roo.bootstrap.Alert = function(config){
25469     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25470     
25471 };
25472
25473 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25474     
25475     title: '',
25476     html: '',
25477     weight: false,
25478     faicon: false,
25479     
25480     getAutoCreate : function()
25481     {
25482         
25483         var cfg = {
25484             tag : 'div',
25485             cls : 'alert',
25486             cn : [
25487                 {
25488                     tag : 'i',
25489                     cls : 'roo-alert-icon'
25490                     
25491                 },
25492                 {
25493                     tag : 'b',
25494                     cls : 'roo-alert-title',
25495                     html : this.title
25496                 },
25497                 {
25498                     tag : 'span',
25499                     cls : 'roo-alert-text',
25500                     html : this.html
25501                 }
25502             ]
25503         };
25504         
25505         if(this.faicon){
25506             cfg.cn[0].cls += ' fa ' + this.faicon;
25507         }
25508         
25509         if(this.weight){
25510             cfg.cls += ' alert-' + this.weight;
25511         }
25512         
25513         return cfg;
25514     },
25515     
25516     initEvents: function() 
25517     {
25518         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25519     },
25520     
25521     setTitle : function(str)
25522     {
25523         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25524     },
25525     
25526     setText : function(str)
25527     {
25528         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25529     },
25530     
25531     setWeight : function(weight)
25532     {
25533         if(this.weight){
25534             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25535         }
25536         
25537         this.weight = weight;
25538         
25539         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25540     },
25541     
25542     setIcon : function(icon)
25543     {
25544         if(this.faicon){
25545             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25546         }
25547         
25548         this.faicon = icon;
25549         
25550         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25551     },
25552     
25553     hide: function() 
25554     {
25555         this.el.hide();   
25556     },
25557     
25558     show: function() 
25559     {  
25560         this.el.show();   
25561     }
25562     
25563 });
25564
25565  
25566 /*
25567 * Licence: LGPL
25568 */
25569
25570 /**
25571  * @class Roo.bootstrap.UploadCropbox
25572  * @extends Roo.bootstrap.Component
25573  * Bootstrap UploadCropbox class
25574  * @cfg {String} emptyText show when image has been loaded
25575  * @cfg {String} rotateNotify show when image too small to rotate
25576  * @cfg {Number} errorTimeout default 3000
25577  * @cfg {Number} minWidth default 300
25578  * @cfg {Number} minHeight default 300
25579  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25580  * @cfg {Boolean} isDocument (true|false) default false
25581  * @cfg {String} url action url
25582  * @cfg {String} paramName default 'imageUpload'
25583  * @cfg {String} method default POST
25584  * @cfg {Boolean} loadMask (true|false) default true
25585  * @cfg {Boolean} loadingText default 'Loading...'
25586  * 
25587  * @constructor
25588  * Create a new UploadCropbox
25589  * @param {Object} config The config object
25590  */
25591
25592 Roo.bootstrap.UploadCropbox = function(config){
25593     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25594     
25595     this.addEvents({
25596         /**
25597          * @event beforeselectfile
25598          * Fire before select file
25599          * @param {Roo.bootstrap.UploadCropbox} this
25600          */
25601         "beforeselectfile" : true,
25602         /**
25603          * @event initial
25604          * Fire after initEvent
25605          * @param {Roo.bootstrap.UploadCropbox} this
25606          */
25607         "initial" : true,
25608         /**
25609          * @event crop
25610          * Fire after initEvent
25611          * @param {Roo.bootstrap.UploadCropbox} this
25612          * @param {String} data
25613          */
25614         "crop" : true,
25615         /**
25616          * @event prepare
25617          * Fire when preparing the file data
25618          * @param {Roo.bootstrap.UploadCropbox} this
25619          * @param {Object} file
25620          */
25621         "prepare" : true,
25622         /**
25623          * @event exception
25624          * Fire when get exception
25625          * @param {Roo.bootstrap.UploadCropbox} this
25626          * @param {XMLHttpRequest} xhr
25627          */
25628         "exception" : true,
25629         /**
25630          * @event beforeloadcanvas
25631          * Fire before load the canvas
25632          * @param {Roo.bootstrap.UploadCropbox} this
25633          * @param {String} src
25634          */
25635         "beforeloadcanvas" : true,
25636         /**
25637          * @event trash
25638          * Fire when trash image
25639          * @param {Roo.bootstrap.UploadCropbox} this
25640          */
25641         "trash" : true,
25642         /**
25643          * @event download
25644          * Fire when download the image
25645          * @param {Roo.bootstrap.UploadCropbox} this
25646          */
25647         "download" : true,
25648         /**
25649          * @event footerbuttonclick
25650          * Fire when footerbuttonclick
25651          * @param {Roo.bootstrap.UploadCropbox} this
25652          * @param {String} type
25653          */
25654         "footerbuttonclick" : true,
25655         /**
25656          * @event resize
25657          * Fire when resize
25658          * @param {Roo.bootstrap.UploadCropbox} this
25659          */
25660         "resize" : true,
25661         /**
25662          * @event rotate
25663          * Fire when rotate the image
25664          * @param {Roo.bootstrap.UploadCropbox} this
25665          * @param {String} pos
25666          */
25667         "rotate" : true,
25668         /**
25669          * @event inspect
25670          * Fire when inspect the file
25671          * @param {Roo.bootstrap.UploadCropbox} this
25672          * @param {Object} file
25673          */
25674         "inspect" : true,
25675         /**
25676          * @event upload
25677          * Fire when xhr upload the file
25678          * @param {Roo.bootstrap.UploadCropbox} this
25679          * @param {Object} data
25680          */
25681         "upload" : true,
25682         /**
25683          * @event arrange
25684          * Fire when arrange the file data
25685          * @param {Roo.bootstrap.UploadCropbox} this
25686          * @param {Object} formData
25687          */
25688         "arrange" : true
25689     });
25690     
25691     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25692 };
25693
25694 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25695     
25696     emptyText : 'Click to upload image',
25697     rotateNotify : 'Image is too small to rotate',
25698     errorTimeout : 3000,
25699     scale : 0,
25700     baseScale : 1,
25701     rotate : 0,
25702     dragable : false,
25703     pinching : false,
25704     mouseX : 0,
25705     mouseY : 0,
25706     cropData : false,
25707     minWidth : 300,
25708     minHeight : 300,
25709     file : false,
25710     exif : {},
25711     baseRotate : 1,
25712     cropType : 'image/jpeg',
25713     buttons : false,
25714     canvasLoaded : false,
25715     isDocument : false,
25716     method : 'POST',
25717     paramName : 'imageUpload',
25718     loadMask : true,
25719     loadingText : 'Loading...',
25720     maskEl : false,
25721     
25722     getAutoCreate : function()
25723     {
25724         var cfg = {
25725             tag : 'div',
25726             cls : 'roo-upload-cropbox',
25727             cn : [
25728                 {
25729                     tag : 'input',
25730                     cls : 'roo-upload-cropbox-selector',
25731                     type : 'file'
25732                 },
25733                 {
25734                     tag : 'div',
25735                     cls : 'roo-upload-cropbox-body',
25736                     style : 'cursor:pointer',
25737                     cn : [
25738                         {
25739                             tag : 'div',
25740                             cls : 'roo-upload-cropbox-preview'
25741                         },
25742                         {
25743                             tag : 'div',
25744                             cls : 'roo-upload-cropbox-thumb'
25745                         },
25746                         {
25747                             tag : 'div',
25748                             cls : 'roo-upload-cropbox-empty-notify',
25749                             html : this.emptyText
25750                         },
25751                         {
25752                             tag : 'div',
25753                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25754                             html : this.rotateNotify
25755                         }
25756                     ]
25757                 },
25758                 {
25759                     tag : 'div',
25760                     cls : 'roo-upload-cropbox-footer',
25761                     cn : {
25762                         tag : 'div',
25763                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25764                         cn : []
25765                     }
25766                 }
25767             ]
25768         };
25769         
25770         return cfg;
25771     },
25772     
25773     onRender : function(ct, position)
25774     {
25775         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25776         
25777         if (this.buttons.length) {
25778             
25779             Roo.each(this.buttons, function(bb) {
25780                 
25781                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25782                 
25783                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25784                 
25785             }, this);
25786         }
25787         
25788         if(this.loadMask){
25789             this.maskEl = this.el;
25790         }
25791     },
25792     
25793     initEvents : function()
25794     {
25795         this.urlAPI = (window.createObjectURL && window) || 
25796                                 (window.URL && URL.revokeObjectURL && URL) || 
25797                                 (window.webkitURL && webkitURL);
25798                         
25799         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25800         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25801         
25802         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25803         this.selectorEl.hide();
25804         
25805         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25806         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25807         
25808         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25809         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25810         this.thumbEl.hide();
25811         
25812         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25813         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25814         
25815         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25816         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25817         this.errorEl.hide();
25818         
25819         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25820         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25821         this.footerEl.hide();
25822         
25823         this.setThumbBoxSize();
25824         
25825         this.bind();
25826         
25827         this.resize();
25828         
25829         this.fireEvent('initial', this);
25830     },
25831
25832     bind : function()
25833     {
25834         var _this = this;
25835         
25836         window.addEventListener("resize", function() { _this.resize(); } );
25837         
25838         this.bodyEl.on('click', this.beforeSelectFile, this);
25839         
25840         if(Roo.isTouch){
25841             this.bodyEl.on('touchstart', this.onTouchStart, this);
25842             this.bodyEl.on('touchmove', this.onTouchMove, this);
25843             this.bodyEl.on('touchend', this.onTouchEnd, this);
25844         }
25845         
25846         if(!Roo.isTouch){
25847             this.bodyEl.on('mousedown', this.onMouseDown, this);
25848             this.bodyEl.on('mousemove', this.onMouseMove, this);
25849             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25850             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25851             Roo.get(document).on('mouseup', this.onMouseUp, this);
25852         }
25853         
25854         this.selectorEl.on('change', this.onFileSelected, this);
25855     },
25856     
25857     reset : function()
25858     {    
25859         this.scale = 0;
25860         this.baseScale = 1;
25861         this.rotate = 0;
25862         this.baseRotate = 1;
25863         this.dragable = false;
25864         this.pinching = false;
25865         this.mouseX = 0;
25866         this.mouseY = 0;
25867         this.cropData = false;
25868         this.notifyEl.dom.innerHTML = this.emptyText;
25869         
25870         this.selectorEl.dom.value = '';
25871         
25872     },
25873     
25874     resize : function()
25875     {
25876         if(this.fireEvent('resize', this) != false){
25877             this.setThumbBoxPosition();
25878             this.setCanvasPosition();
25879         }
25880     },
25881     
25882     onFooterButtonClick : function(e, el, o, type)
25883     {
25884         switch (type) {
25885             case 'rotate-left' :
25886                 this.onRotateLeft(e);
25887                 break;
25888             case 'rotate-right' :
25889                 this.onRotateRight(e);
25890                 break;
25891             case 'picture' :
25892                 this.beforeSelectFile(e);
25893                 break;
25894             case 'trash' :
25895                 this.trash(e);
25896                 break;
25897             case 'crop' :
25898                 this.crop(e);
25899                 break;
25900             case 'download' :
25901                 this.download(e);
25902                 break;
25903             default :
25904                 break;
25905         }
25906         
25907         this.fireEvent('footerbuttonclick', this, type);
25908     },
25909     
25910     beforeSelectFile : function(e)
25911     {
25912         e.preventDefault();
25913         
25914         if(this.fireEvent('beforeselectfile', this) != false){
25915             this.selectorEl.dom.click();
25916         }
25917     },
25918     
25919     onFileSelected : function(e)
25920     {
25921         e.preventDefault();
25922         
25923         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25924             return;
25925         }
25926         
25927         var file = this.selectorEl.dom.files[0];
25928         
25929         if(this.fireEvent('inspect', this, file) != false){
25930             this.prepare(file);
25931         }
25932         
25933     },
25934     
25935     trash : function(e)
25936     {
25937         this.fireEvent('trash', this);
25938     },
25939     
25940     download : function(e)
25941     {
25942         this.fireEvent('download', this);
25943     },
25944     
25945     loadCanvas : function(src)
25946     {   
25947         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25948             
25949             this.reset();
25950             
25951             this.imageEl = document.createElement('img');
25952             
25953             var _this = this;
25954             
25955             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25956             
25957             this.imageEl.src = src;
25958         }
25959     },
25960     
25961     onLoadCanvas : function()
25962     {   
25963         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25964         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25965         
25966         this.bodyEl.un('click', this.beforeSelectFile, this);
25967         
25968         this.notifyEl.hide();
25969         this.thumbEl.show();
25970         this.footerEl.show();
25971         
25972         this.baseRotateLevel();
25973         
25974         if(this.isDocument){
25975             this.setThumbBoxSize();
25976         }
25977         
25978         this.setThumbBoxPosition();
25979         
25980         this.baseScaleLevel();
25981         
25982         this.draw();
25983         
25984         this.resize();
25985         
25986         this.canvasLoaded = true;
25987         
25988         if(this.loadMask){
25989             this.maskEl.unmask();
25990         }
25991         
25992     },
25993     
25994     setCanvasPosition : function()
25995     {   
25996         if(!this.canvasEl){
25997             return;
25998         }
25999         
26000         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26001         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26002         
26003         this.previewEl.setLeft(pw);
26004         this.previewEl.setTop(ph);
26005         
26006     },
26007     
26008     onMouseDown : function(e)
26009     {   
26010         e.stopEvent();
26011         
26012         this.dragable = true;
26013         this.pinching = false;
26014         
26015         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26016             this.dragable = false;
26017             return;
26018         }
26019         
26020         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26021         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26022         
26023     },
26024     
26025     onMouseMove : function(e)
26026     {   
26027         e.stopEvent();
26028         
26029         if(!this.canvasLoaded){
26030             return;
26031         }
26032         
26033         if (!this.dragable){
26034             return;
26035         }
26036         
26037         var minX = Math.ceil(this.thumbEl.getLeft(true));
26038         var minY = Math.ceil(this.thumbEl.getTop(true));
26039         
26040         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26041         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26042         
26043         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26044         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26045         
26046         x = x - this.mouseX;
26047         y = y - this.mouseY;
26048         
26049         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26050         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26051         
26052         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26053         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26054         
26055         this.previewEl.setLeft(bgX);
26056         this.previewEl.setTop(bgY);
26057         
26058         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26059         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26060     },
26061     
26062     onMouseUp : function(e)
26063     {   
26064         e.stopEvent();
26065         
26066         this.dragable = false;
26067     },
26068     
26069     onMouseWheel : function(e)
26070     {   
26071         e.stopEvent();
26072         
26073         this.startScale = this.scale;
26074         
26075         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26076         
26077         if(!this.zoomable()){
26078             this.scale = this.startScale;
26079             return;
26080         }
26081         
26082         this.draw();
26083         
26084         return;
26085     },
26086     
26087     zoomable : function()
26088     {
26089         var minScale = this.thumbEl.getWidth() / this.minWidth;
26090         
26091         if(this.minWidth < this.minHeight){
26092             minScale = this.thumbEl.getHeight() / this.minHeight;
26093         }
26094         
26095         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26096         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26097         
26098         if(
26099                 this.isDocument &&
26100                 (this.rotate == 0 || this.rotate == 180) && 
26101                 (
26102                     width > this.imageEl.OriginWidth || 
26103                     height > this.imageEl.OriginHeight ||
26104                     (width < this.minWidth && height < this.minHeight)
26105                 )
26106         ){
26107             return false;
26108         }
26109         
26110         if(
26111                 this.isDocument &&
26112                 (this.rotate == 90 || this.rotate == 270) && 
26113                 (
26114                     width > this.imageEl.OriginWidth || 
26115                     height > this.imageEl.OriginHeight ||
26116                     (width < this.minHeight && height < this.minWidth)
26117                 )
26118         ){
26119             return false;
26120         }
26121         
26122         if(
26123                 !this.isDocument &&
26124                 (this.rotate == 0 || this.rotate == 180) && 
26125                 (
26126                     width < this.minWidth || 
26127                     width > this.imageEl.OriginWidth || 
26128                     height < this.minHeight || 
26129                     height > this.imageEl.OriginHeight
26130                 )
26131         ){
26132             return false;
26133         }
26134         
26135         if(
26136                 !this.isDocument &&
26137                 (this.rotate == 90 || this.rotate == 270) && 
26138                 (
26139                     width < this.minHeight || 
26140                     width > this.imageEl.OriginWidth || 
26141                     height < this.minWidth || 
26142                     height > this.imageEl.OriginHeight
26143                 )
26144         ){
26145             return false;
26146         }
26147         
26148         return true;
26149         
26150     },
26151     
26152     onRotateLeft : function(e)
26153     {   
26154         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26155             
26156             var minScale = this.thumbEl.getWidth() / this.minWidth;
26157             
26158             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26159             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26160             
26161             this.startScale = this.scale;
26162             
26163             while (this.getScaleLevel() < minScale){
26164             
26165                 this.scale = this.scale + 1;
26166                 
26167                 if(!this.zoomable()){
26168                     break;
26169                 }
26170                 
26171                 if(
26172                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26173                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26174                 ){
26175                     continue;
26176                 }
26177                 
26178                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26179
26180                 this.draw();
26181                 
26182                 return;
26183             }
26184             
26185             this.scale = this.startScale;
26186             
26187             this.onRotateFail();
26188             
26189             return false;
26190         }
26191         
26192         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26193
26194         if(this.isDocument){
26195             this.setThumbBoxSize();
26196             this.setThumbBoxPosition();
26197             this.setCanvasPosition();
26198         }
26199         
26200         this.draw();
26201         
26202         this.fireEvent('rotate', this, 'left');
26203         
26204     },
26205     
26206     onRotateRight : function(e)
26207     {
26208         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26209             
26210             var minScale = this.thumbEl.getWidth() / this.minWidth;
26211         
26212             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26213             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26214             
26215             this.startScale = this.scale;
26216             
26217             while (this.getScaleLevel() < minScale){
26218             
26219                 this.scale = this.scale + 1;
26220                 
26221                 if(!this.zoomable()){
26222                     break;
26223                 }
26224                 
26225                 if(
26226                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26227                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26228                 ){
26229                     continue;
26230                 }
26231                 
26232                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26233
26234                 this.draw();
26235                 
26236                 return;
26237             }
26238             
26239             this.scale = this.startScale;
26240             
26241             this.onRotateFail();
26242             
26243             return false;
26244         }
26245         
26246         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26247
26248         if(this.isDocument){
26249             this.setThumbBoxSize();
26250             this.setThumbBoxPosition();
26251             this.setCanvasPosition();
26252         }
26253         
26254         this.draw();
26255         
26256         this.fireEvent('rotate', this, 'right');
26257     },
26258     
26259     onRotateFail : function()
26260     {
26261         this.errorEl.show(true);
26262         
26263         var _this = this;
26264         
26265         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26266     },
26267     
26268     draw : function()
26269     {
26270         this.previewEl.dom.innerHTML = '';
26271         
26272         var canvasEl = document.createElement("canvas");
26273         
26274         var contextEl = canvasEl.getContext("2d");
26275         
26276         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26277         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26278         var center = this.imageEl.OriginWidth / 2;
26279         
26280         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26281             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26282             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26283             center = this.imageEl.OriginHeight / 2;
26284         }
26285         
26286         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26287         
26288         contextEl.translate(center, center);
26289         contextEl.rotate(this.rotate * Math.PI / 180);
26290
26291         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26292         
26293         this.canvasEl = document.createElement("canvas");
26294         
26295         this.contextEl = this.canvasEl.getContext("2d");
26296         
26297         switch (this.rotate) {
26298             case 0 :
26299                 
26300                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26301                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26302                 
26303                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26304                 
26305                 break;
26306             case 90 : 
26307                 
26308                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26309                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26310                 
26311                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26312                     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);
26313                     break;
26314                 }
26315                 
26316                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26317                 
26318                 break;
26319             case 180 :
26320                 
26321                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26322                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26323                 
26324                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26325                     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);
26326                     break;
26327                 }
26328                 
26329                 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);
26330                 
26331                 break;
26332             case 270 :
26333                 
26334                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26335                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26336         
26337                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26338                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26339                     break;
26340                 }
26341                 
26342                 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);
26343                 
26344                 break;
26345             default : 
26346                 break;
26347         }
26348         
26349         this.previewEl.appendChild(this.canvasEl);
26350         
26351         this.setCanvasPosition();
26352     },
26353     
26354     crop : function()
26355     {
26356         if(!this.canvasLoaded){
26357             return;
26358         }
26359         
26360         var imageCanvas = document.createElement("canvas");
26361         
26362         var imageContext = imageCanvas.getContext("2d");
26363         
26364         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26365         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26366         
26367         var center = imageCanvas.width / 2;
26368         
26369         imageContext.translate(center, center);
26370         
26371         imageContext.rotate(this.rotate * Math.PI / 180);
26372         
26373         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26374         
26375         var canvas = document.createElement("canvas");
26376         
26377         var context = canvas.getContext("2d");
26378                 
26379         canvas.width = this.minWidth;
26380         canvas.height = this.minHeight;
26381
26382         switch (this.rotate) {
26383             case 0 :
26384                 
26385                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26386                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26387                 
26388                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26389                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26390                 
26391                 var targetWidth = this.minWidth - 2 * x;
26392                 var targetHeight = this.minHeight - 2 * y;
26393                 
26394                 var scale = 1;
26395                 
26396                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26397                     scale = targetWidth / width;
26398                 }
26399                 
26400                 if(x > 0 && y == 0){
26401                     scale = targetHeight / height;
26402                 }
26403                 
26404                 if(x > 0 && y > 0){
26405                     scale = targetWidth / width;
26406                     
26407                     if(width < height){
26408                         scale = targetHeight / height;
26409                     }
26410                 }
26411                 
26412                 context.scale(scale, scale);
26413                 
26414                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26415                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26416
26417                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26418                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26419
26420                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26421                 
26422                 break;
26423             case 90 : 
26424                 
26425                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26426                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26427                 
26428                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26429                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26430                 
26431                 var targetWidth = this.minWidth - 2 * x;
26432                 var targetHeight = this.minHeight - 2 * y;
26433                 
26434                 var scale = 1;
26435                 
26436                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26437                     scale = targetWidth / width;
26438                 }
26439                 
26440                 if(x > 0 && y == 0){
26441                     scale = targetHeight / height;
26442                 }
26443                 
26444                 if(x > 0 && y > 0){
26445                     scale = targetWidth / width;
26446                     
26447                     if(width < height){
26448                         scale = targetHeight / height;
26449                     }
26450                 }
26451                 
26452                 context.scale(scale, scale);
26453                 
26454                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26455                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26456
26457                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26458                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26459                 
26460                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26461                 
26462                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26463                 
26464                 break;
26465             case 180 :
26466                 
26467                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26468                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26469                 
26470                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26471                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26472                 
26473                 var targetWidth = this.minWidth - 2 * x;
26474                 var targetHeight = this.minHeight - 2 * y;
26475                 
26476                 var scale = 1;
26477                 
26478                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26479                     scale = targetWidth / width;
26480                 }
26481                 
26482                 if(x > 0 && y == 0){
26483                     scale = targetHeight / height;
26484                 }
26485                 
26486                 if(x > 0 && y > 0){
26487                     scale = targetWidth / width;
26488                     
26489                     if(width < height){
26490                         scale = targetHeight / height;
26491                     }
26492                 }
26493                 
26494                 context.scale(scale, scale);
26495                 
26496                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26497                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26498
26499                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26500                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26501
26502                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26503                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26504                 
26505                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26506                 
26507                 break;
26508             case 270 :
26509                 
26510                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26511                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26512                 
26513                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26514                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26515                 
26516                 var targetWidth = this.minWidth - 2 * x;
26517                 var targetHeight = this.minHeight - 2 * y;
26518                 
26519                 var scale = 1;
26520                 
26521                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26522                     scale = targetWidth / width;
26523                 }
26524                 
26525                 if(x > 0 && y == 0){
26526                     scale = targetHeight / height;
26527                 }
26528                 
26529                 if(x > 0 && y > 0){
26530                     scale = targetWidth / width;
26531                     
26532                     if(width < height){
26533                         scale = targetHeight / height;
26534                     }
26535                 }
26536                 
26537                 context.scale(scale, scale);
26538                 
26539                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26540                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26541
26542                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26543                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26544                 
26545                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26546                 
26547                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26548                 
26549                 break;
26550             default : 
26551                 break;
26552         }
26553         
26554         this.cropData = canvas.toDataURL(this.cropType);
26555         
26556         if(this.fireEvent('crop', this, this.cropData) !== false){
26557             this.process(this.file, this.cropData);
26558         }
26559         
26560         return;
26561         
26562     },
26563     
26564     setThumbBoxSize : function()
26565     {
26566         var width, height;
26567         
26568         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26569             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26570             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26571             
26572             this.minWidth = width;
26573             this.minHeight = height;
26574             
26575             if(this.rotate == 90 || this.rotate == 270){
26576                 this.minWidth = height;
26577                 this.minHeight = width;
26578             }
26579         }
26580         
26581         height = 300;
26582         width = Math.ceil(this.minWidth * height / this.minHeight);
26583         
26584         if(this.minWidth > this.minHeight){
26585             width = 300;
26586             height = Math.ceil(this.minHeight * width / this.minWidth);
26587         }
26588         
26589         this.thumbEl.setStyle({
26590             width : width + 'px',
26591             height : height + 'px'
26592         });
26593
26594         return;
26595             
26596     },
26597     
26598     setThumbBoxPosition : function()
26599     {
26600         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26601         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26602         
26603         this.thumbEl.setLeft(x);
26604         this.thumbEl.setTop(y);
26605         
26606     },
26607     
26608     baseRotateLevel : function()
26609     {
26610         this.baseRotate = 1;
26611         
26612         if(
26613                 typeof(this.exif) != 'undefined' &&
26614                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26615                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26616         ){
26617             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26618         }
26619         
26620         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26621         
26622     },
26623     
26624     baseScaleLevel : function()
26625     {
26626         var width, height;
26627         
26628         if(this.isDocument){
26629             
26630             if(this.baseRotate == 6 || this.baseRotate == 8){
26631             
26632                 height = this.thumbEl.getHeight();
26633                 this.baseScale = height / this.imageEl.OriginWidth;
26634
26635                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26636                     width = this.thumbEl.getWidth();
26637                     this.baseScale = width / this.imageEl.OriginHeight;
26638                 }
26639
26640                 return;
26641             }
26642
26643             height = this.thumbEl.getHeight();
26644             this.baseScale = height / this.imageEl.OriginHeight;
26645
26646             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26647                 width = this.thumbEl.getWidth();
26648                 this.baseScale = width / this.imageEl.OriginWidth;
26649             }
26650
26651             return;
26652         }
26653         
26654         if(this.baseRotate == 6 || this.baseRotate == 8){
26655             
26656             width = this.thumbEl.getHeight();
26657             this.baseScale = width / this.imageEl.OriginHeight;
26658             
26659             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26660                 height = this.thumbEl.getWidth();
26661                 this.baseScale = height / this.imageEl.OriginHeight;
26662             }
26663             
26664             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26665                 height = this.thumbEl.getWidth();
26666                 this.baseScale = height / this.imageEl.OriginHeight;
26667                 
26668                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26669                     width = this.thumbEl.getHeight();
26670                     this.baseScale = width / this.imageEl.OriginWidth;
26671                 }
26672             }
26673             
26674             return;
26675         }
26676         
26677         width = this.thumbEl.getWidth();
26678         this.baseScale = width / this.imageEl.OriginWidth;
26679         
26680         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26681             height = this.thumbEl.getHeight();
26682             this.baseScale = height / this.imageEl.OriginHeight;
26683         }
26684         
26685         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26686             
26687             height = this.thumbEl.getHeight();
26688             this.baseScale = height / this.imageEl.OriginHeight;
26689             
26690             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26691                 width = this.thumbEl.getWidth();
26692                 this.baseScale = width / this.imageEl.OriginWidth;
26693             }
26694             
26695         }
26696         
26697         return;
26698     },
26699     
26700     getScaleLevel : function()
26701     {
26702         return this.baseScale * Math.pow(1.1, this.scale);
26703     },
26704     
26705     onTouchStart : function(e)
26706     {
26707         if(!this.canvasLoaded){
26708             this.beforeSelectFile(e);
26709             return;
26710         }
26711         
26712         var touches = e.browserEvent.touches;
26713         
26714         if(!touches){
26715             return;
26716         }
26717         
26718         if(touches.length == 1){
26719             this.onMouseDown(e);
26720             return;
26721         }
26722         
26723         if(touches.length != 2){
26724             return;
26725         }
26726         
26727         var coords = [];
26728         
26729         for(var i = 0, finger; finger = touches[i]; i++){
26730             coords.push(finger.pageX, finger.pageY);
26731         }
26732         
26733         var x = Math.pow(coords[0] - coords[2], 2);
26734         var y = Math.pow(coords[1] - coords[3], 2);
26735         
26736         this.startDistance = Math.sqrt(x + y);
26737         
26738         this.startScale = this.scale;
26739         
26740         this.pinching = true;
26741         this.dragable = false;
26742         
26743     },
26744     
26745     onTouchMove : function(e)
26746     {
26747         if(!this.pinching && !this.dragable){
26748             return;
26749         }
26750         
26751         var touches = e.browserEvent.touches;
26752         
26753         if(!touches){
26754             return;
26755         }
26756         
26757         if(this.dragable){
26758             this.onMouseMove(e);
26759             return;
26760         }
26761         
26762         var coords = [];
26763         
26764         for(var i = 0, finger; finger = touches[i]; i++){
26765             coords.push(finger.pageX, finger.pageY);
26766         }
26767         
26768         var x = Math.pow(coords[0] - coords[2], 2);
26769         var y = Math.pow(coords[1] - coords[3], 2);
26770         
26771         this.endDistance = Math.sqrt(x + y);
26772         
26773         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26774         
26775         if(!this.zoomable()){
26776             this.scale = this.startScale;
26777             return;
26778         }
26779         
26780         this.draw();
26781         
26782     },
26783     
26784     onTouchEnd : function(e)
26785     {
26786         this.pinching = false;
26787         this.dragable = false;
26788         
26789     },
26790     
26791     process : function(file, crop)
26792     {
26793         if(this.loadMask){
26794             this.maskEl.mask(this.loadingText);
26795         }
26796         
26797         this.xhr = new XMLHttpRequest();
26798         
26799         file.xhr = this.xhr;
26800
26801         this.xhr.open(this.method, this.url, true);
26802         
26803         var headers = {
26804             "Accept": "application/json",
26805             "Cache-Control": "no-cache",
26806             "X-Requested-With": "XMLHttpRequest"
26807         };
26808         
26809         for (var headerName in headers) {
26810             var headerValue = headers[headerName];
26811             if (headerValue) {
26812                 this.xhr.setRequestHeader(headerName, headerValue);
26813             }
26814         }
26815         
26816         var _this = this;
26817         
26818         this.xhr.onload = function()
26819         {
26820             _this.xhrOnLoad(_this.xhr);
26821         }
26822         
26823         this.xhr.onerror = function()
26824         {
26825             _this.xhrOnError(_this.xhr);
26826         }
26827         
26828         var formData = new FormData();
26829
26830         formData.append('returnHTML', 'NO');
26831         
26832         if(crop){
26833             formData.append('crop', crop);
26834         }
26835         
26836         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26837             formData.append(this.paramName, file, file.name);
26838         }
26839         
26840         if(typeof(file.filename) != 'undefined'){
26841             formData.append('filename', file.filename);
26842         }
26843         
26844         if(typeof(file.mimetype) != 'undefined'){
26845             formData.append('mimetype', file.mimetype);
26846         }
26847         
26848         if(this.fireEvent('arrange', this, formData) != false){
26849             this.xhr.send(formData);
26850         };
26851     },
26852     
26853     xhrOnLoad : function(xhr)
26854     {
26855         if(this.loadMask){
26856             this.maskEl.unmask();
26857         }
26858         
26859         if (xhr.readyState !== 4) {
26860             this.fireEvent('exception', this, xhr);
26861             return;
26862         }
26863
26864         var response = Roo.decode(xhr.responseText);
26865         
26866         if(!response.success){
26867             this.fireEvent('exception', this, xhr);
26868             return;
26869         }
26870         
26871         var response = Roo.decode(xhr.responseText);
26872         
26873         this.fireEvent('upload', this, response);
26874         
26875     },
26876     
26877     xhrOnError : function()
26878     {
26879         if(this.loadMask){
26880             this.maskEl.unmask();
26881         }
26882         
26883         Roo.log('xhr on error');
26884         
26885         var response = Roo.decode(xhr.responseText);
26886           
26887         Roo.log(response);
26888         
26889     },
26890     
26891     prepare : function(file)
26892     {   
26893         if(this.loadMask){
26894             this.maskEl.mask(this.loadingText);
26895         }
26896         
26897         this.file = false;
26898         this.exif = {};
26899         
26900         if(typeof(file) === 'string'){
26901             this.loadCanvas(file);
26902             return;
26903         }
26904         
26905         if(!file || !this.urlAPI){
26906             return;
26907         }
26908         
26909         this.file = file;
26910         this.cropType = file.type;
26911         
26912         var _this = this;
26913         
26914         if(this.fireEvent('prepare', this, this.file) != false){
26915             
26916             var reader = new FileReader();
26917             
26918             reader.onload = function (e) {
26919                 if (e.target.error) {
26920                     Roo.log(e.target.error);
26921                     return;
26922                 }
26923                 
26924                 var buffer = e.target.result,
26925                     dataView = new DataView(buffer),
26926                     offset = 2,
26927                     maxOffset = dataView.byteLength - 4,
26928                     markerBytes,
26929                     markerLength;
26930                 
26931                 if (dataView.getUint16(0) === 0xffd8) {
26932                     while (offset < maxOffset) {
26933                         markerBytes = dataView.getUint16(offset);
26934                         
26935                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26936                             markerLength = dataView.getUint16(offset + 2) + 2;
26937                             if (offset + markerLength > dataView.byteLength) {
26938                                 Roo.log('Invalid meta data: Invalid segment size.');
26939                                 break;
26940                             }
26941                             
26942                             if(markerBytes == 0xffe1){
26943                                 _this.parseExifData(
26944                                     dataView,
26945                                     offset,
26946                                     markerLength
26947                                 );
26948                             }
26949                             
26950                             offset += markerLength;
26951                             
26952                             continue;
26953                         }
26954                         
26955                         break;
26956                     }
26957                     
26958                 }
26959                 
26960                 var url = _this.urlAPI.createObjectURL(_this.file);
26961                 
26962                 _this.loadCanvas(url);
26963                 
26964                 return;
26965             }
26966             
26967             reader.readAsArrayBuffer(this.file);
26968             
26969         }
26970         
26971     },
26972     
26973     parseExifData : function(dataView, offset, length)
26974     {
26975         var tiffOffset = offset + 10,
26976             littleEndian,
26977             dirOffset;
26978     
26979         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26980             // No Exif data, might be XMP data instead
26981             return;
26982         }
26983         
26984         // Check for the ASCII code for "Exif" (0x45786966):
26985         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26986             // No Exif data, might be XMP data instead
26987             return;
26988         }
26989         if (tiffOffset + 8 > dataView.byteLength) {
26990             Roo.log('Invalid Exif data: Invalid segment size.');
26991             return;
26992         }
26993         // Check for the two null bytes:
26994         if (dataView.getUint16(offset + 8) !== 0x0000) {
26995             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26996             return;
26997         }
26998         // Check the byte alignment:
26999         switch (dataView.getUint16(tiffOffset)) {
27000         case 0x4949:
27001             littleEndian = true;
27002             break;
27003         case 0x4D4D:
27004             littleEndian = false;
27005             break;
27006         default:
27007             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27008             return;
27009         }
27010         // Check for the TIFF tag marker (0x002A):
27011         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27012             Roo.log('Invalid Exif data: Missing TIFF marker.');
27013             return;
27014         }
27015         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27016         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27017         
27018         this.parseExifTags(
27019             dataView,
27020             tiffOffset,
27021             tiffOffset + dirOffset,
27022             littleEndian
27023         );
27024     },
27025     
27026     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27027     {
27028         var tagsNumber,
27029             dirEndOffset,
27030             i;
27031         if (dirOffset + 6 > dataView.byteLength) {
27032             Roo.log('Invalid Exif data: Invalid directory offset.');
27033             return;
27034         }
27035         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27036         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27037         if (dirEndOffset + 4 > dataView.byteLength) {
27038             Roo.log('Invalid Exif data: Invalid directory size.');
27039             return;
27040         }
27041         for (i = 0; i < tagsNumber; i += 1) {
27042             this.parseExifTag(
27043                 dataView,
27044                 tiffOffset,
27045                 dirOffset + 2 + 12 * i, // tag offset
27046                 littleEndian
27047             );
27048         }
27049         // Return the offset to the next directory:
27050         return dataView.getUint32(dirEndOffset, littleEndian);
27051     },
27052     
27053     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27054     {
27055         var tag = dataView.getUint16(offset, littleEndian);
27056         
27057         this.exif[tag] = this.getExifValue(
27058             dataView,
27059             tiffOffset,
27060             offset,
27061             dataView.getUint16(offset + 2, littleEndian), // tag type
27062             dataView.getUint32(offset + 4, littleEndian), // tag length
27063             littleEndian
27064         );
27065     },
27066     
27067     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27068     {
27069         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27070             tagSize,
27071             dataOffset,
27072             values,
27073             i,
27074             str,
27075             c;
27076     
27077         if (!tagType) {
27078             Roo.log('Invalid Exif data: Invalid tag type.');
27079             return;
27080         }
27081         
27082         tagSize = tagType.size * length;
27083         // Determine if the value is contained in the dataOffset bytes,
27084         // or if the value at the dataOffset is a pointer to the actual data:
27085         dataOffset = tagSize > 4 ?
27086                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27087         if (dataOffset + tagSize > dataView.byteLength) {
27088             Roo.log('Invalid Exif data: Invalid data offset.');
27089             return;
27090         }
27091         if (length === 1) {
27092             return tagType.getValue(dataView, dataOffset, littleEndian);
27093         }
27094         values = [];
27095         for (i = 0; i < length; i += 1) {
27096             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27097         }
27098         
27099         if (tagType.ascii) {
27100             str = '';
27101             // Concatenate the chars:
27102             for (i = 0; i < values.length; i += 1) {
27103                 c = values[i];
27104                 // Ignore the terminating NULL byte(s):
27105                 if (c === '\u0000') {
27106                     break;
27107                 }
27108                 str += c;
27109             }
27110             return str;
27111         }
27112         return values;
27113     }
27114     
27115 });
27116
27117 Roo.apply(Roo.bootstrap.UploadCropbox, {
27118     tags : {
27119         'Orientation': 0x0112
27120     },
27121     
27122     Orientation: {
27123             1: 0, //'top-left',
27124 //            2: 'top-right',
27125             3: 180, //'bottom-right',
27126 //            4: 'bottom-left',
27127 //            5: 'left-top',
27128             6: 90, //'right-top',
27129 //            7: 'right-bottom',
27130             8: 270 //'left-bottom'
27131     },
27132     
27133     exifTagTypes : {
27134         // byte, 8-bit unsigned int:
27135         1: {
27136             getValue: function (dataView, dataOffset) {
27137                 return dataView.getUint8(dataOffset);
27138             },
27139             size: 1
27140         },
27141         // ascii, 8-bit byte:
27142         2: {
27143             getValue: function (dataView, dataOffset) {
27144                 return String.fromCharCode(dataView.getUint8(dataOffset));
27145             },
27146             size: 1,
27147             ascii: true
27148         },
27149         // short, 16 bit int:
27150         3: {
27151             getValue: function (dataView, dataOffset, littleEndian) {
27152                 return dataView.getUint16(dataOffset, littleEndian);
27153             },
27154             size: 2
27155         },
27156         // long, 32 bit int:
27157         4: {
27158             getValue: function (dataView, dataOffset, littleEndian) {
27159                 return dataView.getUint32(dataOffset, littleEndian);
27160             },
27161             size: 4
27162         },
27163         // rational = two long values, first is numerator, second is denominator:
27164         5: {
27165             getValue: function (dataView, dataOffset, littleEndian) {
27166                 return dataView.getUint32(dataOffset, littleEndian) /
27167                     dataView.getUint32(dataOffset + 4, littleEndian);
27168             },
27169             size: 8
27170         },
27171         // slong, 32 bit signed int:
27172         9: {
27173             getValue: function (dataView, dataOffset, littleEndian) {
27174                 return dataView.getInt32(dataOffset, littleEndian);
27175             },
27176             size: 4
27177         },
27178         // srational, two slongs, first is numerator, second is denominator:
27179         10: {
27180             getValue: function (dataView, dataOffset, littleEndian) {
27181                 return dataView.getInt32(dataOffset, littleEndian) /
27182                     dataView.getInt32(dataOffset + 4, littleEndian);
27183             },
27184             size: 8
27185         }
27186     },
27187     
27188     footer : {
27189         STANDARD : [
27190             {
27191                 tag : 'div',
27192                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27193                 action : 'rotate-left',
27194                 cn : [
27195                     {
27196                         tag : 'button',
27197                         cls : 'btn btn-default',
27198                         html : '<i class="fa fa-undo"></i>'
27199                     }
27200                 ]
27201             },
27202             {
27203                 tag : 'div',
27204                 cls : 'btn-group roo-upload-cropbox-picture',
27205                 action : 'picture',
27206                 cn : [
27207                     {
27208                         tag : 'button',
27209                         cls : 'btn btn-default',
27210                         html : '<i class="fa fa-picture-o"></i>'
27211                     }
27212                 ]
27213             },
27214             {
27215                 tag : 'div',
27216                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27217                 action : 'rotate-right',
27218                 cn : [
27219                     {
27220                         tag : 'button',
27221                         cls : 'btn btn-default',
27222                         html : '<i class="fa fa-repeat"></i>'
27223                     }
27224                 ]
27225             }
27226         ],
27227         DOCUMENT : [
27228             {
27229                 tag : 'div',
27230                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27231                 action : 'rotate-left',
27232                 cn : [
27233                     {
27234                         tag : 'button',
27235                         cls : 'btn btn-default',
27236                         html : '<i class="fa fa-undo"></i>'
27237                     }
27238                 ]
27239             },
27240             {
27241                 tag : 'div',
27242                 cls : 'btn-group roo-upload-cropbox-download',
27243                 action : 'download',
27244                 cn : [
27245                     {
27246                         tag : 'button',
27247                         cls : 'btn btn-default',
27248                         html : '<i class="fa fa-download"></i>'
27249                     }
27250                 ]
27251             },
27252             {
27253                 tag : 'div',
27254                 cls : 'btn-group roo-upload-cropbox-crop',
27255                 action : 'crop',
27256                 cn : [
27257                     {
27258                         tag : 'button',
27259                         cls : 'btn btn-default',
27260                         html : '<i class="fa fa-crop"></i>'
27261                     }
27262                 ]
27263             },
27264             {
27265                 tag : 'div',
27266                 cls : 'btn-group roo-upload-cropbox-trash',
27267                 action : 'trash',
27268                 cn : [
27269                     {
27270                         tag : 'button',
27271                         cls : 'btn btn-default',
27272                         html : '<i class="fa fa-trash"></i>'
27273                     }
27274                 ]
27275             },
27276             {
27277                 tag : 'div',
27278                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27279                 action : 'rotate-right',
27280                 cn : [
27281                     {
27282                         tag : 'button',
27283                         cls : 'btn btn-default',
27284                         html : '<i class="fa fa-repeat"></i>'
27285                     }
27286                 ]
27287             }
27288         ],
27289         ROTATOR : [
27290             {
27291                 tag : 'div',
27292                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27293                 action : 'rotate-left',
27294                 cn : [
27295                     {
27296                         tag : 'button',
27297                         cls : 'btn btn-default',
27298                         html : '<i class="fa fa-undo"></i>'
27299                     }
27300                 ]
27301             },
27302             {
27303                 tag : 'div',
27304                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27305                 action : 'rotate-right',
27306                 cn : [
27307                     {
27308                         tag : 'button',
27309                         cls : 'btn btn-default',
27310                         html : '<i class="fa fa-repeat"></i>'
27311                     }
27312                 ]
27313             }
27314         ]
27315     }
27316 });
27317
27318 /*
27319 * Licence: LGPL
27320 */
27321
27322 /**
27323  * @class Roo.bootstrap.DocumentManager
27324  * @extends Roo.bootstrap.Component
27325  * Bootstrap DocumentManager class
27326  * @cfg {String} paramName default 'imageUpload'
27327  * @cfg {String} toolTipName default 'filename'
27328  * @cfg {String} method default POST
27329  * @cfg {String} url action url
27330  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27331  * @cfg {Boolean} multiple multiple upload default true
27332  * @cfg {Number} thumbSize default 300
27333  * @cfg {String} fieldLabel
27334  * @cfg {Number} labelWidth default 4
27335  * @cfg {String} labelAlign (left|top) default left
27336  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27337  * 
27338  * @constructor
27339  * Create a new DocumentManager
27340  * @param {Object} config The config object
27341  */
27342
27343 Roo.bootstrap.DocumentManager = function(config){
27344     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27345     
27346     this.files = [];
27347     this.delegates = [];
27348     
27349     this.addEvents({
27350         /**
27351          * @event initial
27352          * Fire when initial the DocumentManager
27353          * @param {Roo.bootstrap.DocumentManager} this
27354          */
27355         "initial" : true,
27356         /**
27357          * @event inspect
27358          * inspect selected file
27359          * @param {Roo.bootstrap.DocumentManager} this
27360          * @param {File} file
27361          */
27362         "inspect" : true,
27363         /**
27364          * @event exception
27365          * Fire when xhr load exception
27366          * @param {Roo.bootstrap.DocumentManager} this
27367          * @param {XMLHttpRequest} xhr
27368          */
27369         "exception" : true,
27370         /**
27371          * @event afterupload
27372          * Fire when xhr load exception
27373          * @param {Roo.bootstrap.DocumentManager} this
27374          * @param {XMLHttpRequest} xhr
27375          */
27376         "afterupload" : true,
27377         /**
27378          * @event prepare
27379          * prepare the form data
27380          * @param {Roo.bootstrap.DocumentManager} this
27381          * @param {Object} formData
27382          */
27383         "prepare" : true,
27384         /**
27385          * @event remove
27386          * Fire when remove the file
27387          * @param {Roo.bootstrap.DocumentManager} this
27388          * @param {Object} file
27389          */
27390         "remove" : true,
27391         /**
27392          * @event refresh
27393          * Fire after refresh the file
27394          * @param {Roo.bootstrap.DocumentManager} this
27395          */
27396         "refresh" : true,
27397         /**
27398          * @event click
27399          * Fire after click the image
27400          * @param {Roo.bootstrap.DocumentManager} this
27401          * @param {Object} file
27402          */
27403         "click" : true,
27404         /**
27405          * @event edit
27406          * Fire when upload a image and editable set to true
27407          * @param {Roo.bootstrap.DocumentManager} this
27408          * @param {Object} file
27409          */
27410         "edit" : true,
27411         /**
27412          * @event beforeselectfile
27413          * Fire before select file
27414          * @param {Roo.bootstrap.DocumentManager} this
27415          */
27416         "beforeselectfile" : true,
27417         /**
27418          * @event process
27419          * Fire before process file
27420          * @param {Roo.bootstrap.DocumentManager} this
27421          * @param {Object} file
27422          */
27423         "process" : true
27424         
27425     });
27426 };
27427
27428 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27429     
27430     boxes : 0,
27431     inputName : '',
27432     thumbSize : 300,
27433     multiple : true,
27434     files : false,
27435     method : 'POST',
27436     url : '',
27437     paramName : 'imageUpload',
27438     toolTipName : 'filename',
27439     fieldLabel : '',
27440     labelWidth : 4,
27441     labelAlign : 'left',
27442     editable : true,
27443     delegates : false,
27444     xhr : false, 
27445     
27446     getAutoCreate : function()
27447     {   
27448         var managerWidget = {
27449             tag : 'div',
27450             cls : 'roo-document-manager',
27451             cn : [
27452                 {
27453                     tag : 'input',
27454                     cls : 'roo-document-manager-selector',
27455                     type : 'file'
27456                 },
27457                 {
27458                     tag : 'div',
27459                     cls : 'roo-document-manager-uploader',
27460                     cn : [
27461                         {
27462                             tag : 'div',
27463                             cls : 'roo-document-manager-upload-btn',
27464                             html : '<i class="fa fa-plus"></i>'
27465                         }
27466                     ]
27467                     
27468                 }
27469             ]
27470         };
27471         
27472         var content = [
27473             {
27474                 tag : 'div',
27475                 cls : 'column col-md-12',
27476                 cn : managerWidget
27477             }
27478         ];
27479         
27480         if(this.fieldLabel.length){
27481             
27482             content = [
27483                 {
27484                     tag : 'div',
27485                     cls : 'column col-md-12',
27486                     html : this.fieldLabel
27487                 },
27488                 {
27489                     tag : 'div',
27490                     cls : 'column col-md-12',
27491                     cn : managerWidget
27492                 }
27493             ];
27494
27495             if(this.labelAlign == 'left'){
27496                 content = [
27497                     {
27498                         tag : 'div',
27499                         cls : 'column col-md-' + this.labelWidth,
27500                         html : this.fieldLabel
27501                     },
27502                     {
27503                         tag : 'div',
27504                         cls : 'column col-md-' + (12 - this.labelWidth),
27505                         cn : managerWidget
27506                     }
27507                 ];
27508                 
27509             }
27510         }
27511         
27512         var cfg = {
27513             tag : 'div',
27514             cls : 'row clearfix',
27515             cn : content
27516         };
27517         
27518         return cfg;
27519         
27520     },
27521     
27522     initEvents : function()
27523     {
27524         this.managerEl = this.el.select('.roo-document-manager', true).first();
27525         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27526         
27527         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27528         this.selectorEl.hide();
27529         
27530         if(this.multiple){
27531             this.selectorEl.attr('multiple', 'multiple');
27532         }
27533         
27534         this.selectorEl.on('change', this.onFileSelected, this);
27535         
27536         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27537         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27538         
27539         this.uploader.on('click', this.onUploaderClick, this);
27540         
27541         this.renderProgressDialog();
27542         
27543         var _this = this;
27544         
27545         window.addEventListener("resize", function() { _this.refresh(); } );
27546         
27547         this.fireEvent('initial', this);
27548     },
27549     
27550     renderProgressDialog : function()
27551     {
27552         var _this = this;
27553         
27554         this.progressDialog = new Roo.bootstrap.Modal({
27555             cls : 'roo-document-manager-progress-dialog',
27556             allow_close : false,
27557             title : '',
27558             buttons : [
27559                 {
27560                     name  :'cancel',
27561                     weight : 'danger',
27562                     html : 'Cancel'
27563                 }
27564             ], 
27565             listeners : { 
27566                 btnclick : function() {
27567                     _this.uploadCancel();
27568                     this.hide();
27569                 }
27570             }
27571         });
27572          
27573         this.progressDialog.render(Roo.get(document.body));
27574          
27575         this.progress = new Roo.bootstrap.Progress({
27576             cls : 'roo-document-manager-progress',
27577             active : true,
27578             striped : true
27579         });
27580         
27581         this.progress.render(this.progressDialog.getChildContainer());
27582         
27583         this.progressBar = new Roo.bootstrap.ProgressBar({
27584             cls : 'roo-document-manager-progress-bar',
27585             aria_valuenow : 0,
27586             aria_valuemin : 0,
27587             aria_valuemax : 12,
27588             panel : 'success'
27589         });
27590         
27591         this.progressBar.render(this.progress.getChildContainer());
27592     },
27593     
27594     onUploaderClick : function(e)
27595     {
27596         e.preventDefault();
27597      
27598         if(this.fireEvent('beforeselectfile', this) != false){
27599             this.selectorEl.dom.click();
27600         }
27601         
27602     },
27603     
27604     onFileSelected : function(e)
27605     {
27606         e.preventDefault();
27607         
27608         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27609             return;
27610         }
27611         
27612         Roo.each(this.selectorEl.dom.files, function(file){
27613             if(this.fireEvent('inspect', this, file) != false){
27614                 this.files.push(file);
27615             }
27616         }, this);
27617         
27618         this.queue();
27619         
27620     },
27621     
27622     queue : function()
27623     {
27624         this.selectorEl.dom.value = '';
27625         
27626         if(!this.files.length){
27627             return;
27628         }
27629         
27630         if(this.boxes > 0 && this.files.length > this.boxes){
27631             this.files = this.files.slice(0, this.boxes);
27632         }
27633         
27634         this.uploader.show();
27635         
27636         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27637             this.uploader.hide();
27638         }
27639         
27640         var _this = this;
27641         
27642         var files = [];
27643         
27644         var docs = [];
27645         
27646         Roo.each(this.files, function(file){
27647             
27648             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27649                 var f = this.renderPreview(file);
27650                 files.push(f);
27651                 return;
27652             }
27653             
27654             if(file.type.indexOf('image') != -1){
27655                 this.delegates.push(
27656                     (function(){
27657                         _this.process(file);
27658                     }).createDelegate(this)
27659                 );
27660         
27661                 return;
27662             }
27663             
27664             docs.push(
27665                 (function(){
27666                     _this.process(file);
27667                 }).createDelegate(this)
27668             );
27669             
27670         }, this);
27671         
27672         this.files = files;
27673         
27674         this.delegates = this.delegates.concat(docs);
27675         
27676         if(!this.delegates.length){
27677             this.refresh();
27678             return;
27679         }
27680         
27681         this.progressBar.aria_valuemax = this.delegates.length;
27682         
27683         this.arrange();
27684         
27685         return;
27686     },
27687     
27688     arrange : function()
27689     {
27690         if(!this.delegates.length){
27691             this.progressDialog.hide();
27692             this.refresh();
27693             return;
27694         }
27695         
27696         var delegate = this.delegates.shift();
27697         
27698         this.progressDialog.show();
27699         
27700         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27701         
27702         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27703         
27704         delegate();
27705     },
27706     
27707     refresh : function()
27708     {
27709         this.uploader.show();
27710         
27711         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27712             this.uploader.hide();
27713         }
27714         
27715         Roo.isTouch ? this.closable(false) : this.closable(true);
27716         
27717         this.fireEvent('refresh', this);
27718     },
27719     
27720     onRemove : function(e, el, o)
27721     {
27722         e.preventDefault();
27723         
27724         this.fireEvent('remove', this, o);
27725         
27726     },
27727     
27728     remove : function(o)
27729     {
27730         var files = [];
27731         
27732         Roo.each(this.files, function(file){
27733             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27734                 files.push(file);
27735                 return;
27736             }
27737
27738             o.target.remove();
27739
27740         }, this);
27741         
27742         this.files = files;
27743         
27744         this.refresh();
27745     },
27746     
27747     clear : function()
27748     {
27749         Roo.each(this.files, function(file){
27750             if(!file.target){
27751                 return;
27752             }
27753             
27754             file.target.remove();
27755
27756         }, this);
27757         
27758         this.files = [];
27759         
27760         this.refresh();
27761     },
27762     
27763     onClick : function(e, el, o)
27764     {
27765         e.preventDefault();
27766         
27767         this.fireEvent('click', this, o);
27768         
27769     },
27770     
27771     closable : function(closable)
27772     {
27773         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27774             
27775             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27776             
27777             if(closable){
27778                 el.show();
27779                 return;
27780             }
27781             
27782             el.hide();
27783             
27784         }, this);
27785     },
27786     
27787     xhrOnLoad : function(xhr)
27788     {
27789         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27790             el.remove();
27791         }, this);
27792         
27793         if (xhr.readyState !== 4) {
27794             this.arrange();
27795             this.fireEvent('exception', this, xhr);
27796             return;
27797         }
27798
27799         var response = Roo.decode(xhr.responseText);
27800         
27801         if(!response.success){
27802             this.arrange();
27803             this.fireEvent('exception', this, xhr);
27804             return;
27805         }
27806         
27807         var file = this.renderPreview(response.data);
27808         
27809         this.files.push(file);
27810         
27811         this.arrange();
27812         
27813         this.fireEvent('afterupload', this, xhr);
27814         
27815     },
27816     
27817     xhrOnError : function(xhr)
27818     {
27819         Roo.log('xhr on error');
27820         
27821         var response = Roo.decode(xhr.responseText);
27822           
27823         Roo.log(response);
27824         
27825         this.arrange();
27826     },
27827     
27828     process : function(file)
27829     {
27830         if(this.fireEvent('process', this, file) !== false){
27831             if(this.editable && file.type.indexOf('image') != -1){
27832                 this.fireEvent('edit', this, file);
27833                 return;
27834             }
27835
27836             this.uploadStart(file, false);
27837
27838             return;
27839         }
27840         
27841     },
27842     
27843     uploadStart : function(file, crop)
27844     {
27845         this.xhr = new XMLHttpRequest();
27846         
27847         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27848             this.arrange();
27849             return;
27850         }
27851         
27852         file.xhr = this.xhr;
27853             
27854         this.managerEl.createChild({
27855             tag : 'div',
27856             cls : 'roo-document-manager-loading',
27857             cn : [
27858                 {
27859                     tag : 'div',
27860                     tooltip : file.name,
27861                     cls : 'roo-document-manager-thumb',
27862                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27863                 }
27864             ]
27865
27866         });
27867
27868         this.xhr.open(this.method, this.url, true);
27869         
27870         var headers = {
27871             "Accept": "application/json",
27872             "Cache-Control": "no-cache",
27873             "X-Requested-With": "XMLHttpRequest"
27874         };
27875         
27876         for (var headerName in headers) {
27877             var headerValue = headers[headerName];
27878             if (headerValue) {
27879                 this.xhr.setRequestHeader(headerName, headerValue);
27880             }
27881         }
27882         
27883         var _this = this;
27884         
27885         this.xhr.onload = function()
27886         {
27887             _this.xhrOnLoad(_this.xhr);
27888         }
27889         
27890         this.xhr.onerror = function()
27891         {
27892             _this.xhrOnError(_this.xhr);
27893         }
27894         
27895         var formData = new FormData();
27896
27897         formData.append('returnHTML', 'NO');
27898         
27899         if(crop){
27900             formData.append('crop', crop);
27901         }
27902         
27903         formData.append(this.paramName, file, file.name);
27904         
27905         var options = {
27906             file : file, 
27907             manually : false
27908         };
27909         
27910         if(this.fireEvent('prepare', this, formData, options) != false){
27911             
27912             if(options.manually){
27913                 return;
27914             }
27915             
27916             this.xhr.send(formData);
27917             return;
27918         };
27919         
27920         this.uploadCancel();
27921     },
27922     
27923     uploadCancel : function()
27924     {
27925         if (this.xhr) {
27926             this.xhr.abort();
27927         }
27928         
27929         this.delegates = [];
27930         
27931         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27932             el.remove();
27933         }, this);
27934         
27935         this.arrange();
27936     },
27937     
27938     renderPreview : function(file)
27939     {
27940         if(typeof(file.target) != 'undefined' && file.target){
27941             return file;
27942         }
27943         
27944         var previewEl = this.managerEl.createChild({
27945             tag : 'div',
27946             cls : 'roo-document-manager-preview',
27947             cn : [
27948                 {
27949                     tag : 'div',
27950                     tooltip : file[this.toolTipName],
27951                     cls : 'roo-document-manager-thumb',
27952                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27953                 },
27954                 {
27955                     tag : 'button',
27956                     cls : 'close',
27957                     html : '<i class="fa fa-times-circle"></i>'
27958                 }
27959             ]
27960         });
27961
27962         var close = previewEl.select('button.close', true).first();
27963
27964         close.on('click', this.onRemove, this, file);
27965
27966         file.target = previewEl;
27967
27968         var image = previewEl.select('img', true).first();
27969         
27970         var _this = this;
27971         
27972         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27973         
27974         image.on('click', this.onClick, this, file);
27975         
27976         return file;
27977         
27978     },
27979     
27980     onPreviewLoad : function(file, image)
27981     {
27982         if(typeof(file.target) == 'undefined' || !file.target){
27983             return;
27984         }
27985         
27986         var width = image.dom.naturalWidth || image.dom.width;
27987         var height = image.dom.naturalHeight || image.dom.height;
27988         
27989         if(width > height){
27990             file.target.addClass('wide');
27991             return;
27992         }
27993         
27994         file.target.addClass('tall');
27995         return;
27996         
27997     },
27998     
27999     uploadFromSource : function(file, crop)
28000     {
28001         this.xhr = new XMLHttpRequest();
28002         
28003         this.managerEl.createChild({
28004             tag : 'div',
28005             cls : 'roo-document-manager-loading',
28006             cn : [
28007                 {
28008                     tag : 'div',
28009                     tooltip : file.name,
28010                     cls : 'roo-document-manager-thumb',
28011                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28012                 }
28013             ]
28014
28015         });
28016
28017         this.xhr.open(this.method, this.url, true);
28018         
28019         var headers = {
28020             "Accept": "application/json",
28021             "Cache-Control": "no-cache",
28022             "X-Requested-With": "XMLHttpRequest"
28023         };
28024         
28025         for (var headerName in headers) {
28026             var headerValue = headers[headerName];
28027             if (headerValue) {
28028                 this.xhr.setRequestHeader(headerName, headerValue);
28029             }
28030         }
28031         
28032         var _this = this;
28033         
28034         this.xhr.onload = function()
28035         {
28036             _this.xhrOnLoad(_this.xhr);
28037         }
28038         
28039         this.xhr.onerror = function()
28040         {
28041             _this.xhrOnError(_this.xhr);
28042         }
28043         
28044         var formData = new FormData();
28045
28046         formData.append('returnHTML', 'NO');
28047         
28048         formData.append('crop', crop);
28049         
28050         if(typeof(file.filename) != 'undefined'){
28051             formData.append('filename', file.filename);
28052         }
28053         
28054         if(typeof(file.mimetype) != 'undefined'){
28055             formData.append('mimetype', file.mimetype);
28056         }
28057         
28058         if(this.fireEvent('prepare', this, formData) != false){
28059             this.xhr.send(formData);
28060         };
28061     }
28062 });
28063
28064 /*
28065 * Licence: LGPL
28066 */
28067
28068 /**
28069  * @class Roo.bootstrap.DocumentViewer
28070  * @extends Roo.bootstrap.Component
28071  * Bootstrap DocumentViewer class
28072  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28073  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28074  * 
28075  * @constructor
28076  * Create a new DocumentViewer
28077  * @param {Object} config The config object
28078  */
28079
28080 Roo.bootstrap.DocumentViewer = function(config){
28081     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28082     
28083     this.addEvents({
28084         /**
28085          * @event initial
28086          * Fire after initEvent
28087          * @param {Roo.bootstrap.DocumentViewer} this
28088          */
28089         "initial" : true,
28090         /**
28091          * @event click
28092          * Fire after click
28093          * @param {Roo.bootstrap.DocumentViewer} this
28094          */
28095         "click" : true,
28096         /**
28097          * @event download
28098          * Fire after download button
28099          * @param {Roo.bootstrap.DocumentViewer} this
28100          */
28101         "download" : true,
28102         /**
28103          * @event trash
28104          * Fire after trash button
28105          * @param {Roo.bootstrap.DocumentViewer} this
28106          */
28107         "trash" : true
28108         
28109     });
28110 };
28111
28112 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28113     
28114     showDownload : true,
28115     
28116     showTrash : true,
28117     
28118     getAutoCreate : function()
28119     {
28120         var cfg = {
28121             tag : 'div',
28122             cls : 'roo-document-viewer',
28123             cn : [
28124                 {
28125                     tag : 'div',
28126                     cls : 'roo-document-viewer-body',
28127                     cn : [
28128                         {
28129                             tag : 'div',
28130                             cls : 'roo-document-viewer-thumb',
28131                             cn : [
28132                                 {
28133                                     tag : 'img',
28134                                     cls : 'roo-document-viewer-image'
28135                                 }
28136                             ]
28137                         }
28138                     ]
28139                 },
28140                 {
28141                     tag : 'div',
28142                     cls : 'roo-document-viewer-footer',
28143                     cn : {
28144                         tag : 'div',
28145                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28146                         cn : [
28147                             {
28148                                 tag : 'div',
28149                                 cls : 'btn-group roo-document-viewer-download',
28150                                 cn : [
28151                                     {
28152                                         tag : 'button',
28153                                         cls : 'btn btn-default',
28154                                         html : '<i class="fa fa-download"></i>'
28155                                     }
28156                                 ]
28157                             },
28158                             {
28159                                 tag : 'div',
28160                                 cls : 'btn-group roo-document-viewer-trash',
28161                                 cn : [
28162                                     {
28163                                         tag : 'button',
28164                                         cls : 'btn btn-default',
28165                                         html : '<i class="fa fa-trash"></i>'
28166                                     }
28167                                 ]
28168                             }
28169                         ]
28170                     }
28171                 }
28172             ]
28173         };
28174         
28175         return cfg;
28176     },
28177     
28178     initEvents : function()
28179     {
28180         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28181         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28182         
28183         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28184         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28185         
28186         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28187         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28188         
28189         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28190         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28191         
28192         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28193         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28194         
28195         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28196         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28197         
28198         this.bodyEl.on('click', this.onClick, this);
28199         this.downloadBtn.on('click', this.onDownload, this);
28200         this.trashBtn.on('click', this.onTrash, this);
28201         
28202         this.downloadBtn.hide();
28203         this.trashBtn.hide();
28204         
28205         if(this.showDownload){
28206             this.downloadBtn.show();
28207         }
28208         
28209         if(this.showTrash){
28210             this.trashBtn.show();
28211         }
28212         
28213         if(!this.showDownload && !this.showTrash) {
28214             this.footerEl.hide();
28215         }
28216         
28217     },
28218     
28219     initial : function()
28220     {
28221         this.fireEvent('initial', this);
28222         
28223     },
28224     
28225     onClick : function(e)
28226     {
28227         e.preventDefault();
28228         
28229         this.fireEvent('click', this);
28230     },
28231     
28232     onDownload : function(e)
28233     {
28234         e.preventDefault();
28235         
28236         this.fireEvent('download', this);
28237     },
28238     
28239     onTrash : function(e)
28240     {
28241         e.preventDefault();
28242         
28243         this.fireEvent('trash', this);
28244     }
28245     
28246 });
28247 /*
28248  * - LGPL
28249  *
28250  * nav progress bar
28251  * 
28252  */
28253
28254 /**
28255  * @class Roo.bootstrap.NavProgressBar
28256  * @extends Roo.bootstrap.Component
28257  * Bootstrap NavProgressBar class
28258  * 
28259  * @constructor
28260  * Create a new nav progress bar
28261  * @param {Object} config The config object
28262  */
28263
28264 Roo.bootstrap.NavProgressBar = function(config){
28265     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28266
28267     this.bullets = this.bullets || [];
28268    
28269 //    Roo.bootstrap.NavProgressBar.register(this);
28270      this.addEvents({
28271         /**
28272              * @event changed
28273              * Fires when the active item changes
28274              * @param {Roo.bootstrap.NavProgressBar} this
28275              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28276              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28277          */
28278         'changed': true
28279      });
28280     
28281 };
28282
28283 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28284     
28285     bullets : [],
28286     barItems : [],
28287     
28288     getAutoCreate : function()
28289     {
28290         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28291         
28292         cfg = {
28293             tag : 'div',
28294             cls : 'roo-navigation-bar-group',
28295             cn : [
28296                 {
28297                     tag : 'div',
28298                     cls : 'roo-navigation-top-bar'
28299                 },
28300                 {
28301                     tag : 'div',
28302                     cls : 'roo-navigation-bullets-bar',
28303                     cn : [
28304                         {
28305                             tag : 'ul',
28306                             cls : 'roo-navigation-bar'
28307                         }
28308                     ]
28309                 },
28310                 
28311                 {
28312                     tag : 'div',
28313                     cls : 'roo-navigation-bottom-bar'
28314                 }
28315             ]
28316             
28317         };
28318         
28319         return cfg;
28320         
28321     },
28322     
28323     initEvents: function() 
28324     {
28325         
28326     },
28327     
28328     onRender : function(ct, position) 
28329     {
28330         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28331         
28332         if(this.bullets.length){
28333             Roo.each(this.bullets, function(b){
28334                this.addItem(b);
28335             }, this);
28336         }
28337         
28338         this.format();
28339         
28340     },
28341     
28342     addItem : function(cfg)
28343     {
28344         var item = new Roo.bootstrap.NavProgressItem(cfg);
28345         
28346         item.parentId = this.id;
28347         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28348         
28349         if(cfg.html){
28350             var top = new Roo.bootstrap.Element({
28351                 tag : 'div',
28352                 cls : 'roo-navigation-bar-text'
28353             });
28354             
28355             var bottom = new Roo.bootstrap.Element({
28356                 tag : 'div',
28357                 cls : 'roo-navigation-bar-text'
28358             });
28359             
28360             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28361             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28362             
28363             var topText = new Roo.bootstrap.Element({
28364                 tag : 'span',
28365                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28366             });
28367             
28368             var bottomText = new Roo.bootstrap.Element({
28369                 tag : 'span',
28370                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28371             });
28372             
28373             topText.onRender(top.el, null);
28374             bottomText.onRender(bottom.el, null);
28375             
28376             item.topEl = top;
28377             item.bottomEl = bottom;
28378         }
28379         
28380         this.barItems.push(item);
28381         
28382         return item;
28383     },
28384     
28385     getActive : function()
28386     {
28387         var active = false;
28388         
28389         Roo.each(this.barItems, function(v){
28390             
28391             if (!v.isActive()) {
28392                 return;
28393             }
28394             
28395             active = v;
28396             return false;
28397             
28398         });
28399         
28400         return active;
28401     },
28402     
28403     setActiveItem : function(item)
28404     {
28405         var prev = false;
28406         
28407         Roo.each(this.barItems, function(v){
28408             if (v.rid == item.rid) {
28409                 return ;
28410             }
28411             
28412             if (v.isActive()) {
28413                 v.setActive(false);
28414                 prev = v;
28415             }
28416         });
28417
28418         item.setActive(true);
28419         
28420         this.fireEvent('changed', this, item, prev);
28421     },
28422     
28423     getBarItem: function(rid)
28424     {
28425         var ret = false;
28426         
28427         Roo.each(this.barItems, function(e) {
28428             if (e.rid != rid) {
28429                 return;
28430             }
28431             
28432             ret =  e;
28433             return false;
28434         });
28435         
28436         return ret;
28437     },
28438     
28439     indexOfItem : function(item)
28440     {
28441         var index = false;
28442         
28443         Roo.each(this.barItems, function(v, i){
28444             
28445             if (v.rid != item.rid) {
28446                 return;
28447             }
28448             
28449             index = i;
28450             return false
28451         });
28452         
28453         return index;
28454     },
28455     
28456     setActiveNext : function()
28457     {
28458         var i = this.indexOfItem(this.getActive());
28459         
28460         if (i > this.barItems.length) {
28461             return;
28462         }
28463         
28464         this.setActiveItem(this.barItems[i+1]);
28465     },
28466     
28467     setActivePrev : function()
28468     {
28469         var i = this.indexOfItem(this.getActive());
28470         
28471         if (i  < 1) {
28472             return;
28473         }
28474         
28475         this.setActiveItem(this.barItems[i-1]);
28476     },
28477     
28478     format : function()
28479     {
28480         if(!this.barItems.length){
28481             return;
28482         }
28483      
28484         var width = 100 / this.barItems.length;
28485         
28486         Roo.each(this.barItems, function(i){
28487             i.el.setStyle('width', width + '%');
28488             i.topEl.el.setStyle('width', width + '%');
28489             i.bottomEl.el.setStyle('width', width + '%');
28490         }, this);
28491         
28492     }
28493     
28494 });
28495 /*
28496  * - LGPL
28497  *
28498  * Nav Progress Item
28499  * 
28500  */
28501
28502 /**
28503  * @class Roo.bootstrap.NavProgressItem
28504  * @extends Roo.bootstrap.Component
28505  * Bootstrap NavProgressItem class
28506  * @cfg {String} rid the reference id
28507  * @cfg {Boolean} active (true|false) Is item active default false
28508  * @cfg {Boolean} disabled (true|false) Is item active default false
28509  * @cfg {String} html
28510  * @cfg {String} position (top|bottom) text position default bottom
28511  * @cfg {String} icon show icon instead of number
28512  * 
28513  * @constructor
28514  * Create a new NavProgressItem
28515  * @param {Object} config The config object
28516  */
28517 Roo.bootstrap.NavProgressItem = function(config){
28518     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28519     this.addEvents({
28520         // raw events
28521         /**
28522          * @event click
28523          * The raw click event for the entire grid.
28524          * @param {Roo.bootstrap.NavProgressItem} this
28525          * @param {Roo.EventObject} e
28526          */
28527         "click" : true
28528     });
28529    
28530 };
28531
28532 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28533     
28534     rid : '',
28535     active : false,
28536     disabled : false,
28537     html : '',
28538     position : 'bottom',
28539     icon : false,
28540     
28541     getAutoCreate : function()
28542     {
28543         var iconCls = 'roo-navigation-bar-item-icon';
28544         
28545         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28546         
28547         var cfg = {
28548             tag: 'li',
28549             cls: 'roo-navigation-bar-item',
28550             cn : [
28551                 {
28552                     tag : 'i',
28553                     cls : iconCls
28554                 }
28555             ]
28556         };
28557         
28558         if(this.active){
28559             cfg.cls += ' active';
28560         }
28561         if(this.disabled){
28562             cfg.cls += ' disabled';
28563         }
28564         
28565         return cfg;
28566     },
28567     
28568     disable : function()
28569     {
28570         this.setDisabled(true);
28571     },
28572     
28573     enable : function()
28574     {
28575         this.setDisabled(false);
28576     },
28577     
28578     initEvents: function() 
28579     {
28580         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28581         
28582         this.iconEl.on('click', this.onClick, this);
28583     },
28584     
28585     onClick : function(e)
28586     {
28587         e.preventDefault();
28588         
28589         if(this.disabled){
28590             return;
28591         }
28592         
28593         if(this.fireEvent('click', this, e) === false){
28594             return;
28595         };
28596         
28597         this.parent().setActiveItem(this);
28598     },
28599     
28600     isActive: function () 
28601     {
28602         return this.active;
28603     },
28604     
28605     setActive : function(state)
28606     {
28607         if(this.active == state){
28608             return;
28609         }
28610         
28611         this.active = state;
28612         
28613         if (state) {
28614             this.el.addClass('active');
28615             return;
28616         }
28617         
28618         this.el.removeClass('active');
28619         
28620         return;
28621     },
28622     
28623     setDisabled : function(state)
28624     {
28625         if(this.disabled == state){
28626             return;
28627         }
28628         
28629         this.disabled = state;
28630         
28631         if (state) {
28632             this.el.addClass('disabled');
28633             return;
28634         }
28635         
28636         this.el.removeClass('disabled');
28637     },
28638     
28639     tooltipEl : function()
28640     {
28641         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28642     }
28643 });
28644  
28645
28646  /*
28647  * - LGPL
28648  *
28649  * FieldLabel
28650  * 
28651  */
28652
28653 /**
28654  * @class Roo.bootstrap.FieldLabel
28655  * @extends Roo.bootstrap.Component
28656  * Bootstrap FieldLabel class
28657  * @cfg {String} html contents of the element
28658  * @cfg {String} tag tag of the element default label
28659  * @cfg {String} cls class of the element
28660  * @cfg {String} target label target 
28661  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28662  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28663  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28664  * @cfg {String} iconTooltip default "This field is required"
28665  * 
28666  * @constructor
28667  * Create a new FieldLabel
28668  * @param {Object} config The config object
28669  */
28670
28671 Roo.bootstrap.FieldLabel = function(config){
28672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28673     
28674     this.addEvents({
28675             /**
28676              * @event invalid
28677              * Fires after the field has been marked as invalid.
28678              * @param {Roo.form.FieldLabel} this
28679              * @param {String} msg The validation message
28680              */
28681             invalid : true,
28682             /**
28683              * @event valid
28684              * Fires after the field has been validated with no errors.
28685              * @param {Roo.form.FieldLabel} this
28686              */
28687             valid : true
28688         });
28689 };
28690
28691 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28692     
28693     tag: 'label',
28694     cls: '',
28695     html: '',
28696     target: '',
28697     allowBlank : true,
28698     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28699     validClass : 'text-success fa fa-lg fa-check',
28700     iconTooltip : 'This field is required',
28701     
28702     getAutoCreate : function(){
28703         
28704         var cfg = {
28705             tag : this.tag,
28706             cls : 'roo-bootstrap-field-label ' + this.cls,
28707             for : this.target,
28708             cn : [
28709                 {
28710                     tag : 'i',
28711                     cls : '',
28712                     tooltip : this.iconTooltip
28713                 },
28714                 {
28715                     tag : 'span',
28716                     html : this.html
28717                 }
28718             ] 
28719         };
28720         
28721         return cfg;
28722     },
28723     
28724     initEvents: function() 
28725     {
28726         Roo.bootstrap.Element.superclass.initEvents.call(this);
28727         
28728         this.iconEl = this.el.select('i', true).first();
28729         
28730         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28731         
28732         Roo.bootstrap.FieldLabel.register(this);
28733     },
28734     
28735     /**
28736      * Mark this field as valid
28737      */
28738     markValid : function()
28739     {
28740         this.iconEl.show();
28741         
28742         this.iconEl.removeClass(this.invalidClass);
28743         
28744         this.iconEl.addClass(this.validClass);
28745         
28746         this.fireEvent('valid', this);
28747     },
28748     
28749     /**
28750      * Mark this field as invalid
28751      * @param {String} msg The validation message
28752      */
28753     markInvalid : function(msg)
28754     {
28755         this.iconEl.show();
28756         
28757         this.iconEl.removeClass(this.validClass);
28758         
28759         this.iconEl.addClass(this.invalidClass);
28760         
28761         this.fireEvent('invalid', this, msg);
28762     }
28763     
28764    
28765 });
28766
28767 Roo.apply(Roo.bootstrap.FieldLabel, {
28768     
28769     groups: {},
28770     
28771      /**
28772     * register a FieldLabel Group
28773     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28774     */
28775     register : function(label)
28776     {
28777         if(this.groups.hasOwnProperty(label.target)){
28778             return;
28779         }
28780      
28781         this.groups[label.target] = label;
28782         
28783     },
28784     /**
28785     * fetch a FieldLabel Group based on the target
28786     * @param {string} target
28787     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28788     */
28789     get: function(target) {
28790         if (typeof(this.groups[target]) == 'undefined') {
28791             return false;
28792         }
28793         
28794         return this.groups[target] ;
28795     }
28796 });
28797
28798  
28799
28800  /*
28801  * - LGPL
28802  *
28803  * page DateSplitField.
28804  * 
28805  */
28806
28807
28808 /**
28809  * @class Roo.bootstrap.DateSplitField
28810  * @extends Roo.bootstrap.Component
28811  * Bootstrap DateSplitField class
28812  * @cfg {string} fieldLabel - the label associated
28813  * @cfg {Number} labelWidth set the width of label (0-12)
28814  * @cfg {String} labelAlign (top|left)
28815  * @cfg {Boolean} dayAllowBlank (true|false) default false
28816  * @cfg {Boolean} monthAllowBlank (true|false) default false
28817  * @cfg {Boolean} yearAllowBlank (true|false) default false
28818  * @cfg {string} dayPlaceholder 
28819  * @cfg {string} monthPlaceholder
28820  * @cfg {string} yearPlaceholder
28821  * @cfg {string} dayFormat default 'd'
28822  * @cfg {string} monthFormat default 'm'
28823  * @cfg {string} yearFormat default 'Y'
28824
28825  *     
28826  * @constructor
28827  * Create a new DateSplitField
28828  * @param {Object} config The config object
28829  */
28830
28831 Roo.bootstrap.DateSplitField = function(config){
28832     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28833     
28834     this.addEvents({
28835         // raw events
28836          /**
28837          * @event years
28838          * getting the data of years
28839          * @param {Roo.bootstrap.DateSplitField} this
28840          * @param {Object} years
28841          */
28842         "years" : true,
28843         /**
28844          * @event days
28845          * getting the data of days
28846          * @param {Roo.bootstrap.DateSplitField} this
28847          * @param {Object} days
28848          */
28849         "days" : true,
28850         /**
28851          * @event invalid
28852          * Fires after the field has been marked as invalid.
28853          * @param {Roo.form.Field} this
28854          * @param {String} msg The validation message
28855          */
28856         invalid : true,
28857        /**
28858          * @event valid
28859          * Fires after the field has been validated with no errors.
28860          * @param {Roo.form.Field} this
28861          */
28862         valid : true
28863     });
28864 };
28865
28866 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28867     
28868     fieldLabel : '',
28869     labelAlign : 'top',
28870     labelWidth : 3,
28871     dayAllowBlank : false,
28872     monthAllowBlank : false,
28873     yearAllowBlank : false,
28874     dayPlaceholder : '',
28875     monthPlaceholder : '',
28876     yearPlaceholder : '',
28877     dayFormat : 'd',
28878     monthFormat : 'm',
28879     yearFormat : 'Y',
28880     isFormField : true,
28881     
28882     getAutoCreate : function()
28883     {
28884         var cfg = {
28885             tag : 'div',
28886             cls : 'row roo-date-split-field-group',
28887             cn : [
28888                 {
28889                     tag : 'input',
28890                     type : 'hidden',
28891                     cls : 'form-hidden-field roo-date-split-field-group-value',
28892                     name : this.name
28893                 }
28894             ]
28895         };
28896         
28897         if(this.fieldLabel){
28898             cfg.cn.push({
28899                 tag : 'div',
28900                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28901                 cn : [
28902                     {
28903                         tag : 'label',
28904                         html : this.fieldLabel
28905                     }
28906                 ]
28907             });
28908         }
28909         
28910         Roo.each(['day', 'month', 'year'], function(t){
28911             cfg.cn.push({
28912                 tag : 'div',
28913                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28914             });
28915         }, this);
28916         
28917         return cfg;
28918     },
28919     
28920     inputEl: function ()
28921     {
28922         return this.el.select('.roo-date-split-field-group-value', true).first();
28923     },
28924     
28925     onRender : function(ct, position) 
28926     {
28927         var _this = this;
28928         
28929         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28930         
28931         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28932         
28933         this.dayField = new Roo.bootstrap.ComboBox({
28934             allowBlank : this.dayAllowBlank,
28935             alwaysQuery : true,
28936             displayField : 'value',
28937             editable : false,
28938             fieldLabel : '',
28939             forceSelection : true,
28940             mode : 'local',
28941             placeholder : this.dayPlaceholder,
28942             selectOnFocus : true,
28943             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28944             triggerAction : 'all',
28945             typeAhead : true,
28946             valueField : 'value',
28947             store : new Roo.data.SimpleStore({
28948                 data : (function() {    
28949                     var days = [];
28950                     _this.fireEvent('days', _this, days);
28951                     return days;
28952                 })(),
28953                 fields : [ 'value' ]
28954             }),
28955             listeners : {
28956                 select : function (_self, record, index)
28957                 {
28958                     _this.setValue(_this.getValue());
28959                 }
28960             }
28961         });
28962
28963         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28964         
28965         this.monthField = new Roo.bootstrap.MonthField({
28966             after : '<i class=\"fa fa-calendar\"></i>',
28967             allowBlank : this.monthAllowBlank,
28968             placeholder : this.monthPlaceholder,
28969             readOnly : true,
28970             listeners : {
28971                 render : function (_self)
28972                 {
28973                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28974                         e.preventDefault();
28975                         _self.focus();
28976                     });
28977                 },
28978                 select : function (_self, oldvalue, newvalue)
28979                 {
28980                     _this.setValue(_this.getValue());
28981                 }
28982             }
28983         });
28984         
28985         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28986         
28987         this.yearField = new Roo.bootstrap.ComboBox({
28988             allowBlank : this.yearAllowBlank,
28989             alwaysQuery : true,
28990             displayField : 'value',
28991             editable : false,
28992             fieldLabel : '',
28993             forceSelection : true,
28994             mode : 'local',
28995             placeholder : this.yearPlaceholder,
28996             selectOnFocus : true,
28997             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28998             triggerAction : 'all',
28999             typeAhead : true,
29000             valueField : 'value',
29001             store : new Roo.data.SimpleStore({
29002                 data : (function() {
29003                     var years = [];
29004                     _this.fireEvent('years', _this, years);
29005                     return years;
29006                 })(),
29007                 fields : [ 'value' ]
29008             }),
29009             listeners : {
29010                 select : function (_self, record, index)
29011                 {
29012                     _this.setValue(_this.getValue());
29013                 }
29014             }
29015         });
29016
29017         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29018     },
29019     
29020     setValue : function(v, format)
29021     {
29022         this.inputEl.dom.value = v;
29023         
29024         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29025         
29026         var d = Date.parseDate(v, f);
29027         
29028         if(!d){
29029             this.validate();
29030             return;
29031         }
29032         
29033         this.setDay(d.format(this.dayFormat));
29034         this.setMonth(d.format(this.monthFormat));
29035         this.setYear(d.format(this.yearFormat));
29036         
29037         this.validate();
29038         
29039         return;
29040     },
29041     
29042     setDay : function(v)
29043     {
29044         this.dayField.setValue(v);
29045         this.inputEl.dom.value = this.getValue();
29046         this.validate();
29047         return;
29048     },
29049     
29050     setMonth : function(v)
29051     {
29052         this.monthField.setValue(v, true);
29053         this.inputEl.dom.value = this.getValue();
29054         this.validate();
29055         return;
29056     },
29057     
29058     setYear : function(v)
29059     {
29060         this.yearField.setValue(v);
29061         this.inputEl.dom.value = this.getValue();
29062         this.validate();
29063         return;
29064     },
29065     
29066     getDay : function()
29067     {
29068         return this.dayField.getValue();
29069     },
29070     
29071     getMonth : function()
29072     {
29073         return this.monthField.getValue();
29074     },
29075     
29076     getYear : function()
29077     {
29078         return this.yearField.getValue();
29079     },
29080     
29081     getValue : function()
29082     {
29083         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29084         
29085         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29086         
29087         return date;
29088     },
29089     
29090     reset : function()
29091     {
29092         this.setDay('');
29093         this.setMonth('');
29094         this.setYear('');
29095         this.inputEl.dom.value = '';
29096         this.validate();
29097         return;
29098     },
29099     
29100     validate : function()
29101     {
29102         var d = this.dayField.validate();
29103         var m = this.monthField.validate();
29104         var y = this.yearField.validate();
29105         
29106         var valid = true;
29107         
29108         if(
29109                 (!this.dayAllowBlank && !d) ||
29110                 (!this.monthAllowBlank && !m) ||
29111                 (!this.yearAllowBlank && !y)
29112         ){
29113             valid = false;
29114         }
29115         
29116         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29117             return valid;
29118         }
29119         
29120         if(valid){
29121             this.markValid();
29122             return valid;
29123         }
29124         
29125         this.markInvalid();
29126         
29127         return valid;
29128     },
29129     
29130     markValid : function()
29131     {
29132         
29133         var label = this.el.select('label', true).first();
29134         var icon = this.el.select('i.fa-star', true).first();
29135
29136         if(label && icon){
29137             icon.remove();
29138         }
29139         
29140         this.fireEvent('valid', this);
29141     },
29142     
29143      /**
29144      * Mark this field as invalid
29145      * @param {String} msg The validation message
29146      */
29147     markInvalid : function(msg)
29148     {
29149         
29150         var label = this.el.select('label', true).first();
29151         var icon = this.el.select('i.fa-star', true).first();
29152
29153         if(label && !icon){
29154             this.el.select('.roo-date-split-field-label', true).createChild({
29155                 tag : 'i',
29156                 cls : 'text-danger fa fa-lg fa-star',
29157                 tooltip : 'This field is required',
29158                 style : 'margin-right:5px;'
29159             }, label, true);
29160         }
29161         
29162         this.fireEvent('invalid', this, msg);
29163     },
29164     
29165     clearInvalid : function()
29166     {
29167         var label = this.el.select('label', true).first();
29168         var icon = this.el.select('i.fa-star', true).first();
29169
29170         if(label && icon){
29171             icon.remove();
29172         }
29173         
29174         this.fireEvent('valid', this);
29175     },
29176     
29177     getName: function()
29178     {
29179         return this.name;
29180     }
29181     
29182 });
29183
29184  /**
29185  *
29186  * This is based on 
29187  * http://masonry.desandro.com
29188  *
29189  * The idea is to render all the bricks based on vertical width...
29190  *
29191  * The original code extends 'outlayer' - we might need to use that....
29192  * 
29193  */
29194
29195
29196 /**
29197  * @class Roo.bootstrap.LayoutMasonry
29198  * @extends Roo.bootstrap.Component
29199  * Bootstrap Layout Masonry class
29200  * 
29201  * @constructor
29202  * Create a new Element
29203  * @param {Object} config The config object
29204  */
29205
29206 Roo.bootstrap.LayoutMasonry = function(config){
29207     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29208     
29209     this.bricks = [];
29210     
29211 };
29212
29213 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29214     
29215     /**
29216      * @cfg {Boolean} isLayoutInstant = no animation?
29217      */   
29218     isLayoutInstant : false, // needed?
29219    
29220     /**
29221      * @cfg {Number} boxWidth  width of the columns
29222      */   
29223     boxWidth : 450,
29224     
29225       /**
29226      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29227      */   
29228     boxHeight : 0,
29229     
29230     /**
29231      * @cfg {Number} padWidth padding below box..
29232      */   
29233     padWidth : 10, 
29234     
29235     /**
29236      * @cfg {Number} gutter gutter width..
29237      */   
29238     gutter : 10,
29239     
29240      /**
29241      * @cfg {Number} maxCols maximum number of columns
29242      */   
29243     
29244     maxCols: 0,
29245     
29246     /**
29247      * @cfg {Boolean} isAutoInitial defalut true
29248      */   
29249     isAutoInitial : true, 
29250     
29251     containerWidth: 0,
29252     
29253     /**
29254      * @cfg {Boolean} isHorizontal defalut false
29255      */   
29256     isHorizontal : false, 
29257
29258     currentSize : null,
29259     
29260     tag: 'div',
29261     
29262     cls: '',
29263     
29264     bricks: null, //CompositeElement
29265     
29266     cols : 1,
29267     
29268     _isLayoutInited : false,
29269     
29270 //    isAlternative : false, // only use for vertical layout...
29271     
29272     /**
29273      * @cfg {Number} alternativePadWidth padding below box..
29274      */   
29275     alternativePadWidth : 50, 
29276     
29277     getAutoCreate : function(){
29278         
29279         var cfg = {
29280             tag: this.tag,
29281             cls: 'blog-masonary-wrapper ' + this.cls,
29282             cn : {
29283                 cls : 'mas-boxes masonary'
29284             }
29285         };
29286         
29287         return cfg;
29288     },
29289     
29290     getChildContainer: function( )
29291     {
29292         if (this.boxesEl) {
29293             return this.boxesEl;
29294         }
29295         
29296         this.boxesEl = this.el.select('.mas-boxes').first();
29297         
29298         return this.boxesEl;
29299     },
29300     
29301     
29302     initEvents : function()
29303     {
29304         var _this = this;
29305         
29306         if(this.isAutoInitial){
29307             Roo.log('hook children rendered');
29308             this.on('childrenrendered', function() {
29309                 Roo.log('children rendered');
29310                 _this.initial();
29311             } ,this);
29312         }
29313     },
29314     
29315     initial : function()
29316     {
29317         this.currentSize = this.el.getBox(true);
29318         
29319         Roo.EventManager.onWindowResize(this.resize, this); 
29320
29321         if(!this.isAutoInitial){
29322             this.layout();
29323             return;
29324         }
29325         
29326         this.layout();
29327         
29328         return;
29329         //this.layout.defer(500,this);
29330         
29331     },
29332     
29333     resize : function()
29334     {
29335         Roo.log('resize');
29336         
29337         var cs = this.el.getBox(true);
29338         
29339         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29340             Roo.log("no change in with or X");
29341             return;
29342         }
29343         
29344         this.currentSize = cs;
29345         
29346         this.layout();
29347         
29348     },
29349     
29350     layout : function()
29351     {   
29352         this._resetLayout();
29353         
29354         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29355         
29356         this.layoutItems( isInstant );
29357       
29358         this._isLayoutInited = true;
29359         
29360     },
29361     
29362     _resetLayout : function()
29363     {
29364         if(this.isHorizontal){
29365             this.horizontalMeasureColumns();
29366             return;
29367         }
29368         
29369         this.verticalMeasureColumns();
29370         
29371     },
29372     
29373     verticalMeasureColumns : function()
29374     {
29375         this.getContainerWidth();
29376         
29377 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29378 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29379 //            return;
29380 //        }
29381         
29382         var boxWidth = this.boxWidth + this.padWidth;
29383         
29384         if(this.containerWidth < this.boxWidth){
29385             boxWidth = this.containerWidth
29386         }
29387         
29388         var containerWidth = this.containerWidth;
29389         
29390         var cols = Math.floor(containerWidth / boxWidth);
29391         
29392         this.cols = Math.max( cols, 1 );
29393         
29394         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29395         
29396         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29397         
29398         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29399         
29400         this.colWidth = boxWidth + avail - this.padWidth;
29401         
29402         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29403         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29404     },
29405     
29406     horizontalMeasureColumns : function()
29407     {
29408         this.getContainerWidth();
29409         
29410         var boxWidth = this.boxWidth;
29411         
29412         if(this.containerWidth < boxWidth){
29413             boxWidth = this.containerWidth;
29414         }
29415         
29416         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29417         
29418         this.el.setHeight(boxWidth);
29419         
29420     },
29421     
29422     getContainerWidth : function()
29423     {
29424         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29425     },
29426     
29427     layoutItems : function( isInstant )
29428     {
29429         var items = Roo.apply([], this.bricks);
29430         
29431         if(this.isHorizontal){
29432             this._horizontalLayoutItems( items , isInstant );
29433             return;
29434         }
29435         
29436 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29437 //            this._verticalAlternativeLayoutItems( items , isInstant );
29438 //            return;
29439 //        }
29440         
29441         this._verticalLayoutItems( items , isInstant );
29442         
29443     },
29444     
29445     _verticalLayoutItems : function ( items , isInstant)
29446     {
29447         if ( !items || !items.length ) {
29448             return;
29449         }
29450         
29451         var standard = [
29452             ['xs', 'xs', 'xs', 'tall'],
29453             ['xs', 'xs', 'tall'],
29454             ['xs', 'xs', 'sm'],
29455             ['xs', 'xs', 'xs'],
29456             ['xs', 'tall'],
29457             ['xs', 'sm'],
29458             ['xs', 'xs'],
29459             ['xs'],
29460             
29461             ['sm', 'xs', 'xs'],
29462             ['sm', 'xs'],
29463             ['sm'],
29464             
29465             ['tall', 'xs', 'xs', 'xs'],
29466             ['tall', 'xs', 'xs'],
29467             ['tall', 'xs'],
29468             ['tall']
29469             
29470         ];
29471         
29472         var queue = [];
29473         
29474         var boxes = [];
29475         
29476         var box = [];
29477         
29478         Roo.each(items, function(item, k){
29479             
29480             switch (item.size) {
29481                 // these layouts take up a full box,
29482                 case 'md' :
29483                 case 'md-left' :
29484                 case 'md-right' :
29485                 case 'wide' :
29486                     
29487                     if(box.length){
29488                         boxes.push(box);
29489                         box = [];
29490                     }
29491                     
29492                     boxes.push([item]);
29493                     
29494                     break;
29495                     
29496                 case 'xs' :
29497                 case 'sm' :
29498                 case 'tall' :
29499                     
29500                     box.push(item);
29501                     
29502                     break;
29503                 default :
29504                     break;
29505                     
29506             }
29507             
29508         }, this);
29509         
29510         if(box.length){
29511             boxes.push(box);
29512             box = [];
29513         }
29514         
29515         var filterPattern = function(box, length)
29516         {
29517             if(!box.length){
29518                 return;
29519             }
29520             
29521             var match = false;
29522             
29523             var pattern = box.slice(0, length);
29524             
29525             var format = [];
29526             
29527             Roo.each(pattern, function(i){
29528                 format.push(i.size);
29529             }, this);
29530             
29531             Roo.each(standard, function(s){
29532                 
29533                 if(String(s) != String(format)){
29534                     return;
29535                 }
29536                 
29537                 match = true;
29538                 return false;
29539                 
29540             }, this);
29541             
29542             if(!match && length == 1){
29543                 return;
29544             }
29545             
29546             if(!match){
29547                 filterPattern(box, length - 1);
29548                 return;
29549             }
29550                 
29551             queue.push(pattern);
29552
29553             box = box.slice(length, box.length);
29554
29555             filterPattern(box, 4);
29556
29557             return;
29558             
29559         }
29560         
29561         Roo.each(boxes, function(box, k){
29562             
29563             if(!box.length){
29564                 return;
29565             }
29566             
29567             if(box.length == 1){
29568                 queue.push(box);
29569                 return;
29570             }
29571             
29572             filterPattern(box, 4);
29573             
29574         }, this);
29575         
29576         this._processVerticalLayoutQueue( queue, isInstant );
29577         
29578     },
29579     
29580 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29581 //    {
29582 //        if ( !items || !items.length ) {
29583 //            return;
29584 //        }
29585 //
29586 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29587 //        
29588 //    },
29589     
29590     _horizontalLayoutItems : function ( items , isInstant)
29591     {
29592         if ( !items || !items.length || items.length < 3) {
29593             return;
29594         }
29595         
29596         items.reverse();
29597         
29598         var eItems = items.slice(0, 3);
29599         
29600         items = items.slice(3, items.length);
29601         
29602         var standard = [
29603             ['xs', 'xs', 'xs', 'wide'],
29604             ['xs', 'xs', 'wide'],
29605             ['xs', 'xs', 'sm'],
29606             ['xs', 'xs', 'xs'],
29607             ['xs', 'wide'],
29608             ['xs', 'sm'],
29609             ['xs', 'xs'],
29610             ['xs'],
29611             
29612             ['sm', 'xs', 'xs'],
29613             ['sm', 'xs'],
29614             ['sm'],
29615             
29616             ['wide', 'xs', 'xs', 'xs'],
29617             ['wide', 'xs', 'xs'],
29618             ['wide', 'xs'],
29619             ['wide'],
29620             
29621             ['wide-thin']
29622         ];
29623         
29624         var queue = [];
29625         
29626         var boxes = [];
29627         
29628         var box = [];
29629         
29630         Roo.each(items, function(item, k){
29631             
29632             switch (item.size) {
29633                 case 'md' :
29634                 case 'md-left' :
29635                 case 'md-right' :
29636                 case 'tall' :
29637                     
29638                     if(box.length){
29639                         boxes.push(box);
29640                         box = [];
29641                     }
29642                     
29643                     boxes.push([item]);
29644                     
29645                     break;
29646                     
29647                 case 'xs' :
29648                 case 'sm' :
29649                 case 'wide' :
29650                 case 'wide-thin' :
29651                     
29652                     box.push(item);
29653                     
29654                     break;
29655                 default :
29656                     break;
29657                     
29658             }
29659             
29660         }, this);
29661         
29662         if(box.length){
29663             boxes.push(box);
29664             box = [];
29665         }
29666         
29667         var filterPattern = function(box, length)
29668         {
29669             if(!box.length){
29670                 return;
29671             }
29672             
29673             var match = false;
29674             
29675             var pattern = box.slice(0, length);
29676             
29677             var format = [];
29678             
29679             Roo.each(pattern, function(i){
29680                 format.push(i.size);
29681             }, this);
29682             
29683             Roo.each(standard, function(s){
29684                 
29685                 if(String(s) != String(format)){
29686                     return;
29687                 }
29688                 
29689                 match = true;
29690                 return false;
29691                 
29692             }, this);
29693             
29694             if(!match && length == 1){
29695                 return;
29696             }
29697             
29698             if(!match){
29699                 filterPattern(box, length - 1);
29700                 return;
29701             }
29702                 
29703             queue.push(pattern);
29704
29705             box = box.slice(length, box.length);
29706
29707             filterPattern(box, 4);
29708
29709             return;
29710             
29711         }
29712         
29713         Roo.each(boxes, function(box, k){
29714             
29715             if(!box.length){
29716                 return;
29717             }
29718             
29719             if(box.length == 1){
29720                 queue.push(box);
29721                 return;
29722             }
29723             
29724             filterPattern(box, 4);
29725             
29726         }, this);
29727         
29728         
29729         var prune = [];
29730         
29731         var pos = this.el.getBox(true);
29732         
29733         var minX = pos.x;
29734         
29735         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29736         
29737         var hit_end = false;
29738         
29739         Roo.each(queue, function(box){
29740             
29741             if(hit_end){
29742                 
29743                 Roo.each(box, function(b){
29744                 
29745                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29746                     b.el.hide();
29747
29748                 }, this);
29749
29750                 return;
29751             }
29752             
29753             var mx = 0;
29754             
29755             Roo.each(box, function(b){
29756                 
29757                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29758                 b.el.show();
29759
29760                 mx = Math.max(mx, b.x);
29761                 
29762             }, this);
29763             
29764             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29765             
29766             if(maxX < minX){
29767                 
29768                 Roo.each(box, function(b){
29769                 
29770                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29771                     b.el.hide();
29772                     
29773                 }, this);
29774                 
29775                 hit_end = true;
29776                 
29777                 return;
29778             }
29779             
29780             prune.push(box);
29781             
29782         }, this);
29783         
29784         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29785     },
29786     
29787     /** Sets position of item in DOM
29788     * @param {Element} item
29789     * @param {Number} x - horizontal position
29790     * @param {Number} y - vertical position
29791     * @param {Boolean} isInstant - disables transitions
29792     */
29793     _processVerticalLayoutQueue : function( queue, isInstant )
29794     {
29795         var pos = this.el.getBox(true);
29796         var x = pos.x;
29797         var y = pos.y;
29798         var maxY = [];
29799         
29800         for (var i = 0; i < this.cols; i++){
29801             maxY[i] = pos.y;
29802         }
29803         
29804         Roo.each(queue, function(box, k){
29805             
29806             var col = k % this.cols;
29807             
29808             Roo.each(box, function(b,kk){
29809                 
29810                 b.el.position('absolute');
29811                 
29812                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29813                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29814                 
29815                 if(b.size == 'md-left' || b.size == 'md-right'){
29816                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29817                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29818                 }
29819                 
29820                 b.el.setWidth(width);
29821                 b.el.setHeight(height);
29822                 // iframe?
29823                 b.el.select('iframe',true).setSize(width,height);
29824                 
29825             }, this);
29826             
29827             for (var i = 0; i < this.cols; i++){
29828                 
29829                 if(maxY[i] < maxY[col]){
29830                     col = i;
29831                     continue;
29832                 }
29833                 
29834                 col = Math.min(col, i);
29835                 
29836             }
29837             
29838             x = pos.x + col * (this.colWidth + this.padWidth);
29839             
29840             y = maxY[col];
29841             
29842             var positions = [];
29843             
29844             switch (box.length){
29845                 case 1 :
29846                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29847                     break;
29848                 case 2 :
29849                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29850                     break;
29851                 case 3 :
29852                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29853                     break;
29854                 case 4 :
29855                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29856                     break;
29857                 default :
29858                     break;
29859             }
29860             
29861             Roo.each(box, function(b,kk){
29862                 
29863                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29864                 
29865                 var sz = b.el.getSize();
29866                 
29867                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29868                 
29869             }, this);
29870             
29871         }, this);
29872         
29873         var mY = 0;
29874         
29875         for (var i = 0; i < this.cols; i++){
29876             mY = Math.max(mY, maxY[i]);
29877         }
29878         
29879         this.el.setHeight(mY - pos.y);
29880         
29881     },
29882     
29883 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29884 //    {
29885 //        var pos = this.el.getBox(true);
29886 //        var x = pos.x;
29887 //        var y = pos.y;
29888 //        var maxX = pos.right;
29889 //        
29890 //        var maxHeight = 0;
29891 //        
29892 //        Roo.each(items, function(item, k){
29893 //            
29894 //            var c = k % 2;
29895 //            
29896 //            item.el.position('absolute');
29897 //                
29898 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29899 //
29900 //            item.el.setWidth(width);
29901 //
29902 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29903 //
29904 //            item.el.setHeight(height);
29905 //            
29906 //            if(c == 0){
29907 //                item.el.setXY([x, y], isInstant ? false : true);
29908 //            } else {
29909 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29910 //            }
29911 //            
29912 //            y = y + height + this.alternativePadWidth;
29913 //            
29914 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29915 //            
29916 //        }, this);
29917 //        
29918 //        this.el.setHeight(maxHeight);
29919 //        
29920 //    },
29921     
29922     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29923     {
29924         var pos = this.el.getBox(true);
29925         
29926         var minX = pos.x;
29927         var minY = pos.y;
29928         
29929         var maxX = pos.right;
29930         
29931         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29932         
29933         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29934         
29935         Roo.each(queue, function(box, k){
29936             
29937             Roo.each(box, function(b, kk){
29938                 
29939                 b.el.position('absolute');
29940                 
29941                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29942                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29943                 
29944                 if(b.size == 'md-left' || b.size == 'md-right'){
29945                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29946                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29947                 }
29948                 
29949                 b.el.setWidth(width);
29950                 b.el.setHeight(height);
29951                 
29952             }, this);
29953             
29954             if(!box.length){
29955                 return;
29956             }
29957             
29958             var positions = [];
29959             
29960             switch (box.length){
29961                 case 1 :
29962                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29963                     break;
29964                 case 2 :
29965                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29966                     break;
29967                 case 3 :
29968                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29969                     break;
29970                 case 4 :
29971                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29972                     break;
29973                 default :
29974                     break;
29975             }
29976             
29977             Roo.each(box, function(b,kk){
29978                 
29979                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29980                 
29981                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29982                 
29983             }, this);
29984             
29985         }, this);
29986         
29987     },
29988     
29989     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29990     {
29991         Roo.each(eItems, function(b,k){
29992             
29993             b.size = (k == 0) ? 'sm' : 'xs';
29994             b.x = (k == 0) ? 2 : 1;
29995             b.y = (k == 0) ? 2 : 1;
29996             
29997             b.el.position('absolute');
29998             
29999             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30000                 
30001             b.el.setWidth(width);
30002             
30003             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30004             
30005             b.el.setHeight(height);
30006             
30007         }, this);
30008
30009         var positions = [];
30010         
30011         positions.push({
30012             x : maxX - this.unitWidth * 2 - this.gutter,
30013             y : minY
30014         });
30015         
30016         positions.push({
30017             x : maxX - this.unitWidth,
30018             y : minY + (this.unitWidth + this.gutter) * 2
30019         });
30020         
30021         positions.push({
30022             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30023             y : minY
30024         });
30025         
30026         Roo.each(eItems, function(b,k){
30027             
30028             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30029
30030         }, this);
30031         
30032     },
30033     
30034     getVerticalOneBoxColPositions : function(x, y, box)
30035     {
30036         var pos = [];
30037         
30038         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30039         
30040         if(box[0].size == 'md-left'){
30041             rand = 0;
30042         }
30043         
30044         if(box[0].size == 'md-right'){
30045             rand = 1;
30046         }
30047         
30048         pos.push({
30049             x : x + (this.unitWidth + this.gutter) * rand,
30050             y : y
30051         });
30052         
30053         return pos;
30054     },
30055     
30056     getVerticalTwoBoxColPositions : function(x, y, box)
30057     {
30058         var pos = [];
30059         
30060         if(box[0].size == 'xs'){
30061             
30062             pos.push({
30063                 x : x,
30064                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30065             });
30066
30067             pos.push({
30068                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30069                 y : y
30070             });
30071             
30072             return pos;
30073             
30074         }
30075         
30076         pos.push({
30077             x : x,
30078             y : y
30079         });
30080
30081         pos.push({
30082             x : x + (this.unitWidth + this.gutter) * 2,
30083             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30084         });
30085         
30086         return pos;
30087         
30088     },
30089     
30090     getVerticalThreeBoxColPositions : function(x, y, box)
30091     {
30092         var pos = [];
30093         
30094         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30095             
30096             pos.push({
30097                 x : x,
30098                 y : y
30099             });
30100
30101             pos.push({
30102                 x : x + (this.unitWidth + this.gutter) * 1,
30103                 y : y
30104             });
30105             
30106             pos.push({
30107                 x : x + (this.unitWidth + this.gutter) * 2,
30108                 y : y
30109             });
30110             
30111             return pos;
30112             
30113         }
30114         
30115         if(box[0].size == 'xs' && box[1].size == 'xs'){
30116             
30117             pos.push({
30118                 x : x,
30119                 y : y
30120             });
30121
30122             pos.push({
30123                 x : x,
30124                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30125             });
30126             
30127             pos.push({
30128                 x : x + (this.unitWidth + this.gutter) * 1,
30129                 y : y
30130             });
30131             
30132             return pos;
30133             
30134         }
30135         
30136         pos.push({
30137             x : x,
30138             y : y
30139         });
30140
30141         pos.push({
30142             x : x + (this.unitWidth + this.gutter) * 2,
30143             y : y
30144         });
30145
30146         pos.push({
30147             x : x + (this.unitWidth + this.gutter) * 2,
30148             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30149         });
30150             
30151         return pos;
30152         
30153     },
30154     
30155     getVerticalFourBoxColPositions : function(x, y, box)
30156     {
30157         var pos = [];
30158         
30159         if(box[0].size == 'xs'){
30160             
30161             pos.push({
30162                 x : x,
30163                 y : y
30164             });
30165
30166             pos.push({
30167                 x : x,
30168                 y : y + (this.unitHeight + this.gutter) * 1
30169             });
30170             
30171             pos.push({
30172                 x : x,
30173                 y : y + (this.unitHeight + this.gutter) * 2
30174             });
30175             
30176             pos.push({
30177                 x : x + (this.unitWidth + this.gutter) * 1,
30178                 y : y
30179             });
30180             
30181             return pos;
30182             
30183         }
30184         
30185         pos.push({
30186             x : x,
30187             y : y
30188         });
30189
30190         pos.push({
30191             x : x + (this.unitWidth + this.gutter) * 2,
30192             y : y
30193         });
30194
30195         pos.push({
30196             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30197             y : y + (this.unitHeight + this.gutter) * 1
30198         });
30199
30200         pos.push({
30201             x : x + (this.unitWidth + this.gutter) * 2,
30202             y : y + (this.unitWidth + this.gutter) * 2
30203         });
30204
30205         return pos;
30206         
30207     },
30208     
30209     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30210     {
30211         var pos = [];
30212         
30213         if(box[0].size == 'md-left'){
30214             pos.push({
30215                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30216                 y : minY
30217             });
30218             
30219             return pos;
30220         }
30221         
30222         if(box[0].size == 'md-right'){
30223             pos.push({
30224                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30225                 y : minY + (this.unitWidth + this.gutter) * 1
30226             });
30227             
30228             return pos;
30229         }
30230         
30231         var rand = Math.floor(Math.random() * (4 - box[0].y));
30232         
30233         pos.push({
30234             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30235             y : minY + (this.unitWidth + this.gutter) * rand
30236         });
30237         
30238         return pos;
30239         
30240     },
30241     
30242     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30243     {
30244         var pos = [];
30245         
30246         if(box[0].size == 'xs'){
30247             
30248             pos.push({
30249                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30250                 y : minY
30251             });
30252
30253             pos.push({
30254                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30255                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30256             });
30257             
30258             return pos;
30259             
30260         }
30261         
30262         pos.push({
30263             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30264             y : minY
30265         });
30266
30267         pos.push({
30268             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30269             y : minY + (this.unitWidth + this.gutter) * 2
30270         });
30271         
30272         return pos;
30273         
30274     },
30275     
30276     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30277     {
30278         var pos = [];
30279         
30280         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30281             
30282             pos.push({
30283                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30284                 y : minY
30285             });
30286
30287             pos.push({
30288                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30289                 y : minY + (this.unitWidth + this.gutter) * 1
30290             });
30291             
30292             pos.push({
30293                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30294                 y : minY + (this.unitWidth + this.gutter) * 2
30295             });
30296             
30297             return pos;
30298             
30299         }
30300         
30301         if(box[0].size == 'xs' && box[1].size == 'xs'){
30302             
30303             pos.push({
30304                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30305                 y : minY
30306             });
30307
30308             pos.push({
30309                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30310                 y : minY
30311             });
30312             
30313             pos.push({
30314                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30315                 y : minY + (this.unitWidth + this.gutter) * 1
30316             });
30317             
30318             return pos;
30319             
30320         }
30321         
30322         pos.push({
30323             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30324             y : minY
30325         });
30326
30327         pos.push({
30328             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30329             y : minY + (this.unitWidth + this.gutter) * 2
30330         });
30331
30332         pos.push({
30333             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30334             y : minY + (this.unitWidth + this.gutter) * 2
30335         });
30336             
30337         return pos;
30338         
30339     },
30340     
30341     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30342     {
30343         var pos = [];
30344         
30345         if(box[0].size == 'xs'){
30346             
30347             pos.push({
30348                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30349                 y : minY
30350             });
30351
30352             pos.push({
30353                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30354                 y : minY
30355             });
30356             
30357             pos.push({
30358                 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),
30359                 y : minY
30360             });
30361             
30362             pos.push({
30363                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30364                 y : minY + (this.unitWidth + this.gutter) * 1
30365             });
30366             
30367             return pos;
30368             
30369         }
30370         
30371         pos.push({
30372             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30373             y : minY
30374         });
30375         
30376         pos.push({
30377             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30378             y : minY + (this.unitWidth + this.gutter) * 2
30379         });
30380         
30381         pos.push({
30382             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30383             y : minY + (this.unitWidth + this.gutter) * 2
30384         });
30385         
30386         pos.push({
30387             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),
30388             y : minY + (this.unitWidth + this.gutter) * 2
30389         });
30390
30391         return pos;
30392         
30393     }
30394     
30395 });
30396
30397  
30398
30399  /**
30400  *
30401  * This is based on 
30402  * http://masonry.desandro.com
30403  *
30404  * The idea is to render all the bricks based on vertical width...
30405  *
30406  * The original code extends 'outlayer' - we might need to use that....
30407  * 
30408  */
30409
30410
30411 /**
30412  * @class Roo.bootstrap.LayoutMasonryAuto
30413  * @extends Roo.bootstrap.Component
30414  * Bootstrap Layout Masonry class
30415  * 
30416  * @constructor
30417  * Create a new Element
30418  * @param {Object} config The config object
30419  */
30420
30421 Roo.bootstrap.LayoutMasonryAuto = function(config){
30422     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30423 };
30424
30425 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30426     
30427       /**
30428      * @cfg {Boolean} isFitWidth  - resize the width..
30429      */   
30430     isFitWidth : false,  // options..
30431     /**
30432      * @cfg {Boolean} isOriginLeft = left align?
30433      */   
30434     isOriginLeft : true,
30435     /**
30436      * @cfg {Boolean} isOriginTop = top align?
30437      */   
30438     isOriginTop : false,
30439     /**
30440      * @cfg {Boolean} isLayoutInstant = no animation?
30441      */   
30442     isLayoutInstant : false, // needed?
30443     /**
30444      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30445      */   
30446     isResizingContainer : true,
30447     /**
30448      * @cfg {Number} columnWidth  width of the columns 
30449      */   
30450     
30451     columnWidth : 0,
30452     
30453     /**
30454      * @cfg {Number} maxCols maximum number of columns
30455      */   
30456     
30457     maxCols: 0,
30458     /**
30459      * @cfg {Number} padHeight padding below box..
30460      */   
30461     
30462     padHeight : 10, 
30463     
30464     /**
30465      * @cfg {Boolean} isAutoInitial defalut true
30466      */   
30467     
30468     isAutoInitial : true, 
30469     
30470     // private?
30471     gutter : 0,
30472     
30473     containerWidth: 0,
30474     initialColumnWidth : 0,
30475     currentSize : null,
30476     
30477     colYs : null, // array.
30478     maxY : 0,
30479     padWidth: 10,
30480     
30481     
30482     tag: 'div',
30483     cls: '',
30484     bricks: null, //CompositeElement
30485     cols : 0, // array?
30486     // element : null, // wrapped now this.el
30487     _isLayoutInited : null, 
30488     
30489     
30490     getAutoCreate : function(){
30491         
30492         var cfg = {
30493             tag: this.tag,
30494             cls: 'blog-masonary-wrapper ' + this.cls,
30495             cn : {
30496                 cls : 'mas-boxes masonary'
30497             }
30498         };
30499         
30500         return cfg;
30501     },
30502     
30503     getChildContainer: function( )
30504     {
30505         if (this.boxesEl) {
30506             return this.boxesEl;
30507         }
30508         
30509         this.boxesEl = this.el.select('.mas-boxes').first();
30510         
30511         return this.boxesEl;
30512     },
30513     
30514     
30515     initEvents : function()
30516     {
30517         var _this = this;
30518         
30519         if(this.isAutoInitial){
30520             Roo.log('hook children rendered');
30521             this.on('childrenrendered', function() {
30522                 Roo.log('children rendered');
30523                 _this.initial();
30524             } ,this);
30525         }
30526         
30527     },
30528     
30529     initial : function()
30530     {
30531         this.reloadItems();
30532
30533         this.currentSize = this.el.getBox(true);
30534
30535         /// was window resize... - let's see if this works..
30536         Roo.EventManager.onWindowResize(this.resize, this); 
30537
30538         if(!this.isAutoInitial){
30539             this.layout();
30540             return;
30541         }
30542         
30543         this.layout.defer(500,this);
30544     },
30545     
30546     reloadItems: function()
30547     {
30548         this.bricks = this.el.select('.masonry-brick', true);
30549         
30550         this.bricks.each(function(b) {
30551             //Roo.log(b.getSize());
30552             if (!b.attr('originalwidth')) {
30553                 b.attr('originalwidth',  b.getSize().width);
30554             }
30555             
30556         });
30557         
30558         Roo.log(this.bricks.elements.length);
30559     },
30560     
30561     resize : function()
30562     {
30563         Roo.log('resize');
30564         var cs = this.el.getBox(true);
30565         
30566         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30567             Roo.log("no change in with or X");
30568             return;
30569         }
30570         this.currentSize = cs;
30571         this.layout();
30572     },
30573     
30574     layout : function()
30575     {
30576          Roo.log('layout');
30577         this._resetLayout();
30578         //this._manageStamps();
30579       
30580         // don't animate first layout
30581         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30582         this.layoutItems( isInstant );
30583       
30584         // flag for initalized
30585         this._isLayoutInited = true;
30586     },
30587     
30588     layoutItems : function( isInstant )
30589     {
30590         //var items = this._getItemsForLayout( this.items );
30591         // original code supports filtering layout items.. we just ignore it..
30592         
30593         this._layoutItems( this.bricks , isInstant );
30594       
30595         this._postLayout();
30596     },
30597     _layoutItems : function ( items , isInstant)
30598     {
30599        //this.fireEvent( 'layout', this, items );
30600     
30601
30602         if ( !items || !items.elements.length ) {
30603           // no items, emit event with empty array
30604             return;
30605         }
30606
30607         var queue = [];
30608         items.each(function(item) {
30609             Roo.log("layout item");
30610             Roo.log(item);
30611             // get x/y object from method
30612             var position = this._getItemLayoutPosition( item );
30613             // enqueue
30614             position.item = item;
30615             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30616             queue.push( position );
30617         }, this);
30618       
30619         this._processLayoutQueue( queue );
30620     },
30621     /** Sets position of item in DOM
30622     * @param {Element} item
30623     * @param {Number} x - horizontal position
30624     * @param {Number} y - vertical position
30625     * @param {Boolean} isInstant - disables transitions
30626     */
30627     _processLayoutQueue : function( queue )
30628     {
30629         for ( var i=0, len = queue.length; i < len; i++ ) {
30630             var obj = queue[i];
30631             obj.item.position('absolute');
30632             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30633         }
30634     },
30635       
30636     
30637     /**
30638     * Any logic you want to do after each layout,
30639     * i.e. size the container
30640     */
30641     _postLayout : function()
30642     {
30643         this.resizeContainer();
30644     },
30645     
30646     resizeContainer : function()
30647     {
30648         if ( !this.isResizingContainer ) {
30649             return;
30650         }
30651         var size = this._getContainerSize();
30652         if ( size ) {
30653             this.el.setSize(size.width,size.height);
30654             this.boxesEl.setSize(size.width,size.height);
30655         }
30656     },
30657     
30658     
30659     
30660     _resetLayout : function()
30661     {
30662         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30663         this.colWidth = this.el.getWidth();
30664         //this.gutter = this.el.getWidth(); 
30665         
30666         this.measureColumns();
30667
30668         // reset column Y
30669         var i = this.cols;
30670         this.colYs = [];
30671         while (i--) {
30672             this.colYs.push( 0 );
30673         }
30674     
30675         this.maxY = 0;
30676     },
30677
30678     measureColumns : function()
30679     {
30680         this.getContainerWidth();
30681       // if columnWidth is 0, default to outerWidth of first item
30682         if ( !this.columnWidth ) {
30683             var firstItem = this.bricks.first();
30684             Roo.log(firstItem);
30685             this.columnWidth  = this.containerWidth;
30686             if (firstItem && firstItem.attr('originalwidth') ) {
30687                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30688             }
30689             // columnWidth fall back to item of first element
30690             Roo.log("set column width?");
30691                         this.initialColumnWidth = this.columnWidth  ;
30692
30693             // if first elem has no width, default to size of container
30694             
30695         }
30696         
30697         
30698         if (this.initialColumnWidth) {
30699             this.columnWidth = this.initialColumnWidth;
30700         }
30701         
30702         
30703             
30704         // column width is fixed at the top - however if container width get's smaller we should
30705         // reduce it...
30706         
30707         // this bit calcs how man columns..
30708             
30709         var columnWidth = this.columnWidth += this.gutter;
30710       
30711         // calculate columns
30712         var containerWidth = this.containerWidth + this.gutter;
30713         
30714         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30715         // fix rounding errors, typically with gutters
30716         var excess = columnWidth - containerWidth % columnWidth;
30717         
30718         
30719         // if overshoot is less than a pixel, round up, otherwise floor it
30720         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30721         cols = Math[ mathMethod ]( cols );
30722         this.cols = Math.max( cols, 1 );
30723         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30724         
30725          // padding positioning..
30726         var totalColWidth = this.cols * this.columnWidth;
30727         var padavail = this.containerWidth - totalColWidth;
30728         // so for 2 columns - we need 3 'pads'
30729         
30730         var padNeeded = (1+this.cols) * this.padWidth;
30731         
30732         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30733         
30734         this.columnWidth += padExtra
30735         //this.padWidth = Math.floor(padavail /  ( this.cols));
30736         
30737         // adjust colum width so that padding is fixed??
30738         
30739         // we have 3 columns ... total = width * 3
30740         // we have X left over... that should be used by 
30741         
30742         //if (this.expandC) {
30743             
30744         //}
30745         
30746         
30747         
30748     },
30749     
30750     getContainerWidth : function()
30751     {
30752        /* // container is parent if fit width
30753         var container = this.isFitWidth ? this.element.parentNode : this.element;
30754         // check that this.size and size are there
30755         // IE8 triggers resize on body size change, so they might not be
30756         
30757         var size = getSize( container );  //FIXME
30758         this.containerWidth = size && size.innerWidth; //FIXME
30759         */
30760          
30761         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30762         
30763     },
30764     
30765     _getItemLayoutPosition : function( item )  // what is item?
30766     {
30767         // we resize the item to our columnWidth..
30768       
30769         item.setWidth(this.columnWidth);
30770         item.autoBoxAdjust  = false;
30771         
30772         var sz = item.getSize();
30773  
30774         // how many columns does this brick span
30775         var remainder = this.containerWidth % this.columnWidth;
30776         
30777         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30778         // round if off by 1 pixel, otherwise use ceil
30779         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30780         colSpan = Math.min( colSpan, this.cols );
30781         
30782         // normally this should be '1' as we dont' currently allow multi width columns..
30783         
30784         var colGroup = this._getColGroup( colSpan );
30785         // get the minimum Y value from the columns
30786         var minimumY = Math.min.apply( Math, colGroup );
30787         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30788         
30789         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30790          
30791         // position the brick
30792         var position = {
30793             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30794             y: this.currentSize.y + minimumY + this.padHeight
30795         };
30796         
30797         Roo.log(position);
30798         // apply setHeight to necessary columns
30799         var setHeight = minimumY + sz.height + this.padHeight;
30800         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30801         
30802         var setSpan = this.cols + 1 - colGroup.length;
30803         for ( var i = 0; i < setSpan; i++ ) {
30804           this.colYs[ shortColIndex + i ] = setHeight ;
30805         }
30806       
30807         return position;
30808     },
30809     
30810     /**
30811      * @param {Number} colSpan - number of columns the element spans
30812      * @returns {Array} colGroup
30813      */
30814     _getColGroup : function( colSpan )
30815     {
30816         if ( colSpan < 2 ) {
30817           // if brick spans only one column, use all the column Ys
30818           return this.colYs;
30819         }
30820       
30821         var colGroup = [];
30822         // how many different places could this brick fit horizontally
30823         var groupCount = this.cols + 1 - colSpan;
30824         // for each group potential horizontal position
30825         for ( var i = 0; i < groupCount; i++ ) {
30826           // make an array of colY values for that one group
30827           var groupColYs = this.colYs.slice( i, i + colSpan );
30828           // and get the max value of the array
30829           colGroup[i] = Math.max.apply( Math, groupColYs );
30830         }
30831         return colGroup;
30832     },
30833     /*
30834     _manageStamp : function( stamp )
30835     {
30836         var stampSize =  stamp.getSize();
30837         var offset = stamp.getBox();
30838         // get the columns that this stamp affects
30839         var firstX = this.isOriginLeft ? offset.x : offset.right;
30840         var lastX = firstX + stampSize.width;
30841         var firstCol = Math.floor( firstX / this.columnWidth );
30842         firstCol = Math.max( 0, firstCol );
30843         
30844         var lastCol = Math.floor( lastX / this.columnWidth );
30845         // lastCol should not go over if multiple of columnWidth #425
30846         lastCol -= lastX % this.columnWidth ? 0 : 1;
30847         lastCol = Math.min( this.cols - 1, lastCol );
30848         
30849         // set colYs to bottom of the stamp
30850         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30851             stampSize.height;
30852             
30853         for ( var i = firstCol; i <= lastCol; i++ ) {
30854           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30855         }
30856     },
30857     */
30858     
30859     _getContainerSize : function()
30860     {
30861         this.maxY = Math.max.apply( Math, this.colYs );
30862         var size = {
30863             height: this.maxY
30864         };
30865       
30866         if ( this.isFitWidth ) {
30867             size.width = this._getContainerFitWidth();
30868         }
30869       
30870         return size;
30871     },
30872     
30873     _getContainerFitWidth : function()
30874     {
30875         var unusedCols = 0;
30876         // count unused columns
30877         var i = this.cols;
30878         while ( --i ) {
30879           if ( this.colYs[i] !== 0 ) {
30880             break;
30881           }
30882           unusedCols++;
30883         }
30884         // fit container to columns that have been used
30885         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30886     },
30887     
30888     needsResizeLayout : function()
30889     {
30890         var previousWidth = this.containerWidth;
30891         this.getContainerWidth();
30892         return previousWidth !== this.containerWidth;
30893     }
30894  
30895 });
30896
30897  
30898
30899  /*
30900  * - LGPL
30901  *
30902  * element
30903  * 
30904  */
30905
30906 /**
30907  * @class Roo.bootstrap.MasonryBrick
30908  * @extends Roo.bootstrap.Component
30909  * Bootstrap MasonryBrick class
30910  * 
30911  * @constructor
30912  * Create a new MasonryBrick
30913  * @param {Object} config The config object
30914  */
30915
30916 Roo.bootstrap.MasonryBrick = function(config){
30917     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30918     
30919     this.addEvents({
30920         // raw events
30921         /**
30922          * @event click
30923          * When a MasonryBrick is clcik
30924          * @param {Roo.bootstrap.MasonryBrick} this
30925          * @param {Roo.EventObject} e
30926          */
30927         "click" : true
30928     });
30929 };
30930
30931 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30932     
30933     /**
30934      * @cfg {String} title
30935      */   
30936     title : '',
30937     /**
30938      * @cfg {String} html
30939      */   
30940     html : '',
30941     /**
30942      * @cfg {String} bgimage
30943      */   
30944     bgimage : '',
30945     /**
30946      * @cfg {String} videourl
30947      */   
30948     videourl : '',
30949     /**
30950      * @cfg {String} cls
30951      */   
30952     cls : '',
30953     /**
30954      * @cfg {String} href
30955      */   
30956     href : '',
30957     /**
30958      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30959      */   
30960     size : 'xs',
30961     
30962     /**
30963      * @cfg {String} (center|bottom) placetitle
30964      */   
30965     placetitle : '',
30966     
30967     /**
30968      * @cfg {Boolean} isFitContainer defalut true
30969      */   
30970     isFitContainer : true, 
30971     
30972     /**
30973      * @cfg {Boolean} preventDefault defalut false
30974      */   
30975     preventDefault : false, 
30976     
30977     getAutoCreate : function()
30978     {
30979         if(!this.isFitContainer){
30980             return this.getSplitAutoCreate();
30981         }
30982         
30983         var cls = 'masonry-brick masonry-brick-full';
30984         
30985         if(this.href.length){
30986             cls += ' masonry-brick-link';
30987         }
30988         
30989         if(this.bgimage.length){
30990             cls += ' masonry-brick-image';
30991         }
30992         
30993         if(!this.html.length){
30994             cls += ' enable-mask';
30995         }
30996         
30997         if(this.size){
30998             cls += ' masonry-' + this.size + '-brick';
30999         }
31000         
31001         if(this.placetitle.length){
31002             
31003             switch (this.placetitle) {
31004                 case 'center' :
31005                     cls += ' masonry-center-title';
31006                     break;
31007                 case 'bottom' :
31008                     cls += ' masonry-bottom-title';
31009                     break;
31010                 default:
31011                     break;
31012             }
31013             
31014         } else {
31015             if(!this.html.length && !this.bgimage.length){
31016                 cls += ' masonry-center-title';
31017             }
31018
31019             if(!this.html.length && this.bgimage.length){
31020                 cls += ' masonry-bottom-title';
31021             }
31022         }
31023         
31024         if(this.cls){
31025             cls += ' ' + this.cls;
31026         }
31027         
31028         var cfg = {
31029             tag: (this.href.length) ? 'a' : 'div',
31030             cls: cls,
31031             cn: [
31032                 {
31033                     tag: 'div',
31034                     cls: 'masonry-brick-paragraph',
31035                     cn: []
31036                 }
31037             ]
31038         };
31039         
31040         if(this.href.length){
31041             cfg.href = this.href;
31042         }
31043         
31044         var cn = cfg.cn[0].cn;
31045         
31046         if(this.title.length){
31047             cn.push({
31048                 tag: 'h4',
31049                 cls: 'masonry-brick-title',
31050                 html: this.title
31051             });
31052         }
31053         
31054         if(this.html.length){
31055             cn.push({
31056                 tag: 'p',
31057                 cls: 'masonry-brick-text',
31058                 html: this.html
31059             });
31060         }  
31061         if (!this.title.length && !this.html.length) {
31062             cfg.cn[0].cls += ' hide';
31063         }
31064         
31065         if(this.bgimage.length){
31066             cfg.cn.push({
31067                 tag: 'img',
31068                 cls: 'masonry-brick-image-view',
31069                 src: this.bgimage
31070             });
31071         }
31072         
31073         if(this.videourl.length){
31074             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31075             // youtube support only?
31076             cfg.cn.push({
31077                 tag: 'iframe',
31078                 cls: 'masonry-brick-image-view',
31079                 src: vurl,
31080                 frameborder : 0,
31081                 allowfullscreen : true
31082             });
31083             
31084             
31085         }
31086         
31087         cfg.cn.push({
31088             tag: 'div',
31089             cls: 'masonry-brick-mask'
31090         });
31091         
31092         return cfg;
31093         
31094     },
31095     
31096     getSplitAutoCreate : function()
31097     {
31098         var cls = 'masonry-brick masonry-brick-split';
31099         
31100         if(this.href.length){
31101             cls += ' masonry-brick-link';
31102         }
31103         
31104         if(this.bgimage.length){
31105             cls += ' masonry-brick-image';
31106         }
31107         
31108         if(this.size){
31109             cls += ' masonry-' + this.size + '-brick';
31110         }
31111         
31112         switch (this.placetitle) {
31113             case 'center' :
31114                 cls += ' masonry-center-title';
31115                 break;
31116             case 'bottom' :
31117                 cls += ' masonry-bottom-title';
31118                 break;
31119             default:
31120                 if(!this.bgimage.length){
31121                     cls += ' masonry-center-title';
31122                 }
31123
31124                 if(this.bgimage.length){
31125                     cls += ' masonry-bottom-title';
31126                 }
31127                 break;
31128         }
31129         
31130         if(this.cls){
31131             cls += ' ' + this.cls;
31132         }
31133         
31134         var cfg = {
31135             tag: (this.href.length) ? 'a' : 'div',
31136             cls: cls,
31137             cn: [
31138                 {
31139                     tag: 'div',
31140                     cls: 'masonry-brick-split-head',
31141                     cn: [
31142                         {
31143                             tag: 'div',
31144                             cls: 'masonry-brick-paragraph',
31145                             cn: []
31146                         }
31147                     ]
31148                 },
31149                 {
31150                     tag: 'div',
31151                     cls: 'masonry-brick-split-body',
31152                     cn: []
31153                 }
31154             ]
31155         };
31156         
31157         if(this.href.length){
31158             cfg.href = this.href;
31159         }
31160         
31161         if(this.title.length){
31162             cfg.cn[0].cn[0].cn.push({
31163                 tag: 'h4',
31164                 cls: 'masonry-brick-title',
31165                 html: this.title
31166             });
31167         }
31168         
31169         if(this.html.length){
31170             cfg.cn[1].cn.push({
31171                 tag: 'p',
31172                 cls: 'masonry-brick-text',
31173                 html: this.html
31174             });
31175         }
31176
31177         if(this.bgimage.length){
31178             cfg.cn[0].cn.push({
31179                 tag: 'img',
31180                 cls: 'masonry-brick-image-view',
31181                 src: this.bgimage
31182             });
31183         }
31184         
31185         if(this.videourl.length){
31186             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31187             // youtube support only?
31188             cfg.cn[0].cn.cn.push({
31189                 tag: 'iframe',
31190                 cls: 'masonry-brick-image-view',
31191                 src: vurl,
31192                 frameborder : 0,
31193                 allowfullscreen : true
31194             });
31195         }
31196         
31197         return cfg;
31198     },
31199     
31200     initEvents: function() 
31201     {
31202         switch (this.size) {
31203             case 'xs' :
31204                 this.x = 1;
31205                 this.y = 1;
31206                 break;
31207             case 'sm' :
31208                 this.x = 2;
31209                 this.y = 2;
31210                 break;
31211             case 'md' :
31212             case 'md-left' :
31213             case 'md-right' :
31214                 this.x = 3;
31215                 this.y = 3;
31216                 break;
31217             case 'tall' :
31218                 this.x = 2;
31219                 this.y = 3;
31220                 break;
31221             case 'wide' :
31222                 this.x = 3;
31223                 this.y = 2;
31224                 break;
31225             case 'wide-thin' :
31226                 this.x = 3;
31227                 this.y = 1;
31228                 break;
31229                         
31230             default :
31231                 break;
31232         }
31233         
31234         if(Roo.isTouch){
31235             this.el.on('touchstart', this.onTouchStart, this);
31236             this.el.on('touchmove', this.onTouchMove, this);
31237             this.el.on('touchend', this.onTouchEnd, this);
31238             this.el.on('contextmenu', this.onContextMenu, this);
31239         } else {
31240             this.el.on('mouseenter'  ,this.enter, this);
31241             this.el.on('mouseleave', this.leave, this);
31242             this.el.on('click', this.onClick, this);
31243         }
31244         
31245         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31246             this.parent().bricks.push(this);   
31247         }
31248         
31249     },
31250     
31251     onClick: function(e, el)
31252     {
31253         var time = this.endTimer - this.startTimer;
31254         
31255         if(Roo.isTouch){
31256             if(time > 1000){
31257                 e.preventDefault();
31258                 return;
31259             }
31260         }
31261         
31262         if(!this.preventDefault){
31263             return;
31264         }
31265         
31266         e.preventDefault();
31267         this.fireEvent('click', this);
31268     },
31269     
31270     enter: function(e, el)
31271     {
31272         e.preventDefault();
31273         
31274         if(!this.isFitContainer){
31275             return;
31276         }
31277         
31278         if(this.bgimage.length && this.html.length){
31279             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31280         }
31281     },
31282     
31283     leave: function(e, el)
31284     {
31285         e.preventDefault();
31286         
31287         if(!this.isFitContainer){
31288             return;
31289         }
31290         
31291         if(this.bgimage.length && this.html.length){
31292             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31293         }
31294     },
31295     
31296     onTouchStart: function(e, el)
31297     {
31298 //        e.preventDefault();
31299         
31300         this.touchmoved = false;
31301         
31302         if(!this.isFitContainer){
31303             return;
31304         }
31305         
31306         if(!this.bgimage.length || !this.html.length){
31307             return;
31308         }
31309         
31310         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31311         
31312         this.timer = new Date().getTime();
31313         
31314     },
31315     
31316     onTouchMove: function(e, el)
31317     {
31318         this.touchmoved = true;
31319     },
31320     
31321     onContextMenu : function(e,el)
31322     {
31323         e.preventDefault();
31324         e.stopPropagation();
31325         return false;
31326     },
31327     
31328     onTouchEnd: function(e, el)
31329     {
31330 //        e.preventDefault();
31331         
31332         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31333         
31334             this.leave(e,el);
31335             
31336             return;
31337         }
31338         
31339         if(!this.bgimage.length || !this.html.length){
31340             
31341             if(this.href.length){
31342                 window.location.href = this.href;
31343             }
31344             
31345             return;
31346         }
31347         
31348         if(!this.isFitContainer){
31349             return;
31350         }
31351         
31352         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31353         
31354         window.location.href = this.href;
31355     }
31356     
31357 });
31358
31359  
31360
31361  /*
31362  * - LGPL
31363  *
31364  * element
31365  * 
31366  */
31367
31368 /**
31369  * @class Roo.bootstrap.Brick
31370  * @extends Roo.bootstrap.Component
31371  * Bootstrap Brick class
31372  * 
31373  * @constructor
31374  * Create a new Brick
31375  * @param {Object} config The config object
31376  */
31377
31378 Roo.bootstrap.Brick = function(config){
31379     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31380     
31381     this.addEvents({
31382         // raw events
31383         /**
31384          * @event click
31385          * When a Brick is click
31386          * @param {Roo.bootstrap.Brick} this
31387          * @param {Roo.EventObject} e
31388          */
31389         "click" : true
31390     });
31391 };
31392
31393 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31394     
31395     /**
31396      * @cfg {String} title
31397      */   
31398     title : '',
31399     /**
31400      * @cfg {String} html
31401      */   
31402     html : '',
31403     /**
31404      * @cfg {String} bgimage
31405      */   
31406     bgimage : '',
31407     /**
31408      * @cfg {String} cls
31409      */   
31410     cls : '',
31411     /**
31412      * @cfg {String} href
31413      */   
31414     href : '',
31415     /**
31416      * @cfg {String} video
31417      */   
31418     video : '',
31419     /**
31420      * @cfg {Boolean} square
31421      */   
31422     square : true,
31423     
31424     getAutoCreate : function()
31425     {
31426         var cls = 'roo-brick';
31427         
31428         if(this.href.length){
31429             cls += ' roo-brick-link';
31430         }
31431         
31432         if(this.bgimage.length){
31433             cls += ' roo-brick-image';
31434         }
31435         
31436         if(!this.html.length && !this.bgimage.length){
31437             cls += ' roo-brick-center-title';
31438         }
31439         
31440         if(!this.html.length && this.bgimage.length){
31441             cls += ' roo-brick-bottom-title';
31442         }
31443         
31444         if(this.cls){
31445             cls += ' ' + this.cls;
31446         }
31447         
31448         var cfg = {
31449             tag: (this.href.length) ? 'a' : 'div',
31450             cls: cls,
31451             cn: [
31452                 {
31453                     tag: 'div',
31454                     cls: 'roo-brick-paragraph',
31455                     cn: []
31456                 }
31457             ]
31458         };
31459         
31460         if(this.href.length){
31461             cfg.href = this.href;
31462         }
31463         
31464         var cn = cfg.cn[0].cn;
31465         
31466         if(this.title.length){
31467             cn.push({
31468                 tag: 'h4',
31469                 cls: 'roo-brick-title',
31470                 html: this.title
31471             });
31472         }
31473         
31474         if(this.html.length){
31475             cn.push({
31476                 tag: 'p',
31477                 cls: 'roo-brick-text',
31478                 html: this.html
31479             });
31480         } else {
31481             cn.cls += ' hide';
31482         }
31483         
31484         if(this.bgimage.length){
31485             cfg.cn.push({
31486                 tag: 'img',
31487                 cls: 'roo-brick-image-view',
31488                 src: this.bgimage
31489             });
31490         }
31491         
31492         return cfg;
31493     },
31494     
31495     initEvents: function() 
31496     {
31497         if(this.title.length || this.html.length){
31498             this.el.on('mouseenter'  ,this.enter, this);
31499             this.el.on('mouseleave', this.leave, this);
31500         }
31501         
31502         
31503         Roo.EventManager.onWindowResize(this.resize, this); 
31504         
31505         this.resize();
31506     },
31507     
31508     resize : function()
31509     {
31510         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31511         
31512         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31513         
31514         if(this.bgimage.length){
31515             var image = this.el.select('.roo-brick-image-view', true).first();
31516             image.setWidth(paragraph.getWidth());
31517             image.setHeight(paragraph.getWidth());
31518             
31519             this.el.setHeight(paragraph.getWidth());
31520             
31521         }
31522         
31523     },
31524     
31525     enter: function(e, el)
31526     {
31527         e.preventDefault();
31528         
31529         if(this.bgimage.length){
31530             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31531             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31532         }
31533     },
31534     
31535     leave: function(e, el)
31536     {
31537         e.preventDefault();
31538         
31539         if(this.bgimage.length){
31540             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31541             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31542         }
31543     }
31544     
31545 });
31546
31547  
31548
31549  /*
31550  * - LGPL
31551  *
31552  * Input
31553  * 
31554  */
31555
31556 /**
31557  * @class Roo.bootstrap.NumberField
31558  * @extends Roo.bootstrap.Input
31559  * Bootstrap NumberField class
31560  * 
31561  * 
31562  * 
31563  * 
31564  * @constructor
31565  * Create a new NumberField
31566  * @param {Object} config The config object
31567  */
31568
31569 Roo.bootstrap.NumberField = function(config){
31570     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31571 };
31572
31573 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31574     
31575     /**
31576      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31577      */
31578     allowDecimals : true,
31579     /**
31580      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31581      */
31582     decimalSeparator : ".",
31583     /**
31584      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31585      */
31586     decimalPrecision : 2,
31587     /**
31588      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31589      */
31590     allowNegative : true,
31591     /**
31592      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31593      */
31594     minValue : Number.NEGATIVE_INFINITY,
31595     /**
31596      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31597      */
31598     maxValue : Number.MAX_VALUE,
31599     /**
31600      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31601      */
31602     minText : "The minimum value for this field is {0}",
31603     /**
31604      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31605      */
31606     maxText : "The maximum value for this field is {0}",
31607     /**
31608      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31609      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31610      */
31611     nanText : "{0} is not a valid number",
31612     /**
31613      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31614      */
31615     castInt : true,
31616
31617     // private
31618     initEvents : function()
31619     {   
31620         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31621         
31622         var allowed = "0123456789";
31623         
31624         if(this.allowDecimals){
31625             allowed += this.decimalSeparator;
31626         }
31627         
31628         if(this.allowNegative){
31629             allowed += "-";
31630         }
31631         
31632         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31633         
31634         var keyPress = function(e){
31635             
31636             var k = e.getKey();
31637             
31638             var c = e.getCharCode();
31639             
31640             if(
31641                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31642                     allowed.indexOf(String.fromCharCode(c)) === -1
31643             ){
31644                 e.stopEvent();
31645                 return;
31646             }
31647             
31648             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31649                 return;
31650             }
31651             
31652             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31653                 e.stopEvent();
31654             }
31655         };
31656         
31657         this.el.on("keypress", keyPress, this);
31658     },
31659     
31660     validateValue : function(value)
31661     {
31662         
31663         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31664             return false;
31665         }
31666         
31667         var num = this.parseValue(value);
31668         
31669         if(isNaN(num)){
31670             this.markInvalid(String.format(this.nanText, value));
31671             return false;
31672         }
31673         
31674         if(num < this.minValue){
31675             this.markInvalid(String.format(this.minText, this.minValue));
31676             return false;
31677         }
31678         
31679         if(num > this.maxValue){
31680             this.markInvalid(String.format(this.maxText, this.maxValue));
31681             return false;
31682         }
31683         
31684         return true;
31685     },
31686
31687     getValue : function()
31688     {
31689         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31690     },
31691
31692     parseValue : function(value)
31693     {
31694         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31695         return isNaN(value) ? '' : value;
31696     },
31697
31698     fixPrecision : function(value)
31699     {
31700         var nan = isNaN(value);
31701         
31702         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31703             return nan ? '' : value;
31704         }
31705         return parseFloat(value).toFixed(this.decimalPrecision);
31706     },
31707
31708     setValue : function(v)
31709     {
31710         v = this.fixPrecision(v);
31711         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31712     },
31713
31714     decimalPrecisionFcn : function(v)
31715     {
31716         return Math.floor(v);
31717     },
31718
31719     beforeBlur : function()
31720     {
31721         if(!this.castInt){
31722             return;
31723         }
31724         
31725         var v = this.parseValue(this.getRawValue());
31726         if(v){
31727             this.setValue(v);
31728         }
31729     }
31730     
31731 });
31732
31733  
31734
31735 /*
31736 * Licence: LGPL
31737 */
31738
31739 /**
31740  * @class Roo.bootstrap.DocumentSlider
31741  * @extends Roo.bootstrap.Component
31742  * Bootstrap DocumentSlider class
31743  * 
31744  * @constructor
31745  * Create a new DocumentViewer
31746  * @param {Object} config The config object
31747  */
31748
31749 Roo.bootstrap.DocumentSlider = function(config){
31750     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31751     
31752     this.files = [];
31753     
31754     this.addEvents({
31755         /**
31756          * @event initial
31757          * Fire after initEvent
31758          * @param {Roo.bootstrap.DocumentSlider} this
31759          */
31760         "initial" : true,
31761         /**
31762          * @event update
31763          * Fire after update
31764          * @param {Roo.bootstrap.DocumentSlider} this
31765          */
31766         "update" : true,
31767         /**
31768          * @event click
31769          * Fire after click
31770          * @param {Roo.bootstrap.DocumentSlider} this
31771          */
31772         "click" : true
31773     });
31774 };
31775
31776 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31777     
31778     files : false,
31779     
31780     indicator : 0,
31781     
31782     getAutoCreate : function()
31783     {
31784         var cfg = {
31785             tag : 'div',
31786             cls : 'roo-document-slider',
31787             cn : [
31788                 {
31789                     tag : 'div',
31790                     cls : 'roo-document-slider-header',
31791                     cn : [
31792                         {
31793                             tag : 'div',
31794                             cls : 'roo-document-slider-header-title'
31795                         }
31796                     ]
31797                 },
31798                 {
31799                     tag : 'div',
31800                     cls : 'roo-document-slider-body',
31801                     cn : [
31802                         {
31803                             tag : 'div',
31804                             cls : 'roo-document-slider-prev',
31805                             cn : [
31806                                 {
31807                                     tag : 'i',
31808                                     cls : 'fa fa-chevron-left'
31809                                 }
31810                             ]
31811                         },
31812                         {
31813                             tag : 'div',
31814                             cls : 'roo-document-slider-thumb',
31815                             cn : [
31816                                 {
31817                                     tag : 'img',
31818                                     cls : 'roo-document-slider-image'
31819                                 }
31820                             ]
31821                         },
31822                         {
31823                             tag : 'div',
31824                             cls : 'roo-document-slider-next',
31825                             cn : [
31826                                 {
31827                                     tag : 'i',
31828                                     cls : 'fa fa-chevron-right'
31829                                 }
31830                             ]
31831                         }
31832                     ]
31833                 }
31834             ]
31835         };
31836         
31837         return cfg;
31838     },
31839     
31840     initEvents : function()
31841     {
31842         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31843         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31844         
31845         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31846         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31847         
31848         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31849         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31850         
31851         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31852         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31853         
31854         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31855         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31856         
31857         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31858         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31859         
31860         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31861         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31862         
31863         this.thumbEl.on('click', this.onClick, this);
31864         
31865         this.prevIndicator.on('click', this.prev, this);
31866         
31867         this.nextIndicator.on('click', this.next, this);
31868         
31869     },
31870     
31871     initial : function()
31872     {
31873         if(this.files.length){
31874             this.indicator = 1;
31875             this.update()
31876         }
31877         
31878         this.fireEvent('initial', this);
31879     },
31880     
31881     update : function()
31882     {
31883         this.imageEl.attr('src', this.files[this.indicator - 1]);
31884         
31885         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31886         
31887         this.prevIndicator.show();
31888         
31889         if(this.indicator == 1){
31890             this.prevIndicator.hide();
31891         }
31892         
31893         this.nextIndicator.show();
31894         
31895         if(this.indicator == this.files.length){
31896             this.nextIndicator.hide();
31897         }
31898         
31899         this.thumbEl.scrollTo('top');
31900         
31901         this.fireEvent('update', this);
31902     },
31903     
31904     onClick : function(e)
31905     {
31906         e.preventDefault();
31907         
31908         this.fireEvent('click', this);
31909     },
31910     
31911     prev : function(e)
31912     {
31913         e.preventDefault();
31914         
31915         this.indicator = Math.max(1, this.indicator - 1);
31916         
31917         this.update();
31918     },
31919     
31920     next : function(e)
31921     {
31922         e.preventDefault();
31923         
31924         this.indicator = Math.min(this.files.length, this.indicator + 1);
31925         
31926         this.update();
31927     }
31928 });
31929 /*
31930  * - LGPL
31931  *
31932  * RadioSet
31933  *
31934  *
31935  */
31936
31937 /**
31938  * @class Roo.bootstrap.RadioSet
31939  * @extends Roo.bootstrap.Input
31940  * Bootstrap RadioSet class
31941  * @cfg {String} indicatorpos (left|right) default left
31942  * @cfg {Boolean} inline (true|false) inline the element (default true)
31943  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
31944  * @constructor
31945  * Create a new RadioSet
31946  * @param {Object} config The config object
31947  */
31948
31949 Roo.bootstrap.RadioSet = function(config){
31950     
31951     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
31952
31953     this.radioes = [];
31954     
31955     Roo.bootstrap.RadioSet.register(this);
31956     
31957     this.addEvents({
31958         /**
31959         * @event check
31960         * Fires when the element is checked or unchecked.
31961         * @param {Roo.bootstrap.RadioSet} this This radio
31962         * @param {Roo.bootstrap.Radio} item The checked item
31963         */
31964        check : true
31965     });
31966     
31967 };
31968
31969 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
31970
31971     radioes : false,
31972     
31973     inline : true,
31974     
31975     weight : '',
31976     
31977     fieldLabel : '',
31978     
31979     indicatorpos : 'left',
31980     
31981     getAutoCreate : function()
31982     {
31983         var label = {
31984             tag : 'label',
31985             cls : 'roo-radio-set-label',
31986             cn : [
31987                 {
31988                     tag : 'span',
31989                     html : this.fieldLabel
31990                 }
31991             ]
31992         };
31993         
31994         if(this.indicatorpos == 'left'){
31995             label.cn.unshift({
31996                 tag : 'i',
31997                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
31998                 tooltip : 'This field is required'
31999             });
32000         } else {
32001             label.cn.push({
32002                 tag : 'i',
32003                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32004                 tooltip : 'This field is required'
32005             });
32006         }
32007         
32008         var items = {
32009             tag : 'div',
32010             cls : 'roo-radio-set-items'
32011         };
32012         
32013         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32014         
32015         if (align === 'left' && this.fieldLabel.length) {
32016             
32017             label.cls += ' col-md-' + this.labelWidth;
32018             
32019             items = {
32020                 cls : "col-md-" + (12 - this.labelWidth), 
32021                 cn: [
32022                     items
32023                 ]
32024             };
32025         }
32026         
32027         var cfg = {
32028             tag : 'div',
32029             cls : 'roo-radio-set',
32030             cn : [
32031                 {
32032                     tag : 'input',
32033                     cls : 'roo-radio-set-input',
32034                     type : 'hidden',
32035                     name : this.name,
32036                     value : this.value ? this.value :  ''
32037                 },
32038                 label,
32039                 items
32040             ]
32041         };
32042         
32043         if(this.inline) {
32044             cfg.cls += ' roo-radio-set-inline';
32045         }
32046         
32047         return cfg;
32048         
32049     },
32050
32051     initEvents : function()
32052     {
32053         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32054         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32055         
32056         this.indicatorEl().hide();
32057         
32058         this.originalValue = this.getValue();
32059         
32060     },
32061     
32062     inputEl: function ()
32063     {
32064         return this.el.select('.roo-radio-set-input', true).first();
32065     },
32066     
32067     getChildContainer : function()
32068     {
32069         return this.itemsEl;
32070     },
32071     
32072     register : function(item)
32073     {
32074         this.radioes.push(item);
32075         
32076         if(this.inline){
32077             item.el.addClass('radio-inline');
32078         }
32079         
32080     },
32081     
32082     validate : function()
32083     {   
32084         var valid = false;
32085         
32086         Roo.each(this.radioes, function(i){
32087             if(!i.checked){
32088                 return;
32089             }
32090             
32091             valid = true;
32092             return false;
32093         });
32094         
32095         if(this.disabled || this.allowBlank || valid){
32096             this.markValid();
32097             return true;
32098         }
32099         
32100         this.markInvalid();
32101         return false;
32102         
32103     },
32104     
32105     markValid : function()
32106     {
32107         this.indicatorEl().hide();
32108         this.el.removeClass([this.invalidClass, this.validClass]);
32109         this.el.addClass(this.validClass);
32110         
32111         this.fireEvent('valid', this);
32112     },
32113     
32114     markInvalid : function(msg)
32115     {
32116         if(this.allowBlank || this.disabled){
32117             return;
32118         }
32119         
32120         this.indicatorEl().show();
32121         this.el.removeClass([this.invalidClass, this.validClass]);
32122         this.el.addClass(this.invalidClass);
32123         
32124         this.fireEvent('invalid', this, msg);
32125         
32126     },
32127     
32128     setValue : function(v, suppressEvent)
32129     {
32130         Roo.each(this.radioes, function(i){
32131             
32132             i.checked = false;
32133             i.el.removeClass('checked');
32134             
32135             if(i.value == v){
32136                 i.checked = true;
32137                 i.el.addClass('checked');
32138                 
32139                 if(suppressEvent !== true){
32140                     this.fireEvent('check', this, i);
32141                 }
32142             }
32143             
32144         }, this);
32145         
32146         Roo.bootstrap.RadioSet.superclass.setValue.call(this)
32147         
32148     }
32149     
32150 });
32151
32152 Roo.apply(Roo.bootstrap.RadioSet, {
32153     
32154     groups: {},
32155     
32156     register : function(set)
32157     {
32158         this.groups[set.name] = set;
32159     },
32160     
32161     get: function(name) 
32162     {
32163         if (typeof(this.groups[name]) == 'undefined') {
32164             return false;
32165         }
32166         
32167         return this.groups[name] ;
32168     }
32169     
32170 });
32171 /*
32172  * Based on:
32173  * Ext JS Library 1.1.1
32174  * Copyright(c) 2006-2007, Ext JS, LLC.
32175  *
32176  * Originally Released Under LGPL - original licence link has changed is not relivant.
32177  *
32178  * Fork - LGPL
32179  * <script type="text/javascript">
32180  */
32181
32182
32183 /**
32184  * @class Roo.bootstrap.SplitBar
32185  * @extends Roo.util.Observable
32186  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32187  * <br><br>
32188  * Usage:
32189  * <pre><code>
32190 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32191                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32192 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32193 split.minSize = 100;
32194 split.maxSize = 600;
32195 split.animate = true;
32196 split.on('moved', splitterMoved);
32197 </code></pre>
32198  * @constructor
32199  * Create a new SplitBar
32200  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32201  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32202  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32203  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32204                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32205                         position of the SplitBar).
32206  */
32207 Roo.bootstrap.SplitBar = function(cfg){
32208     
32209     /** @private */
32210     
32211     //{
32212     //  dragElement : elm
32213     //  resizingElement: el,
32214         // optional..
32215     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32216     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32217         // existingProxy ???
32218     //}
32219     
32220     this.el = Roo.get(cfg.dragElement, true);
32221     this.el.dom.unselectable = "on";
32222     /** @private */
32223     this.resizingEl = Roo.get(cfg.resizingElement, true);
32224
32225     /**
32226      * @private
32227      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32228      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32229      * @type Number
32230      */
32231     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32232     
32233     /**
32234      * The minimum size of the resizing element. (Defaults to 0)
32235      * @type Number
32236      */
32237     this.minSize = 0;
32238     
32239     /**
32240      * The maximum size of the resizing element. (Defaults to 2000)
32241      * @type Number
32242      */
32243     this.maxSize = 2000;
32244     
32245     /**
32246      * Whether to animate the transition to the new size
32247      * @type Boolean
32248      */
32249     this.animate = false;
32250     
32251     /**
32252      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32253      * @type Boolean
32254      */
32255     this.useShim = false;
32256     
32257     /** @private */
32258     this.shim = null;
32259     
32260     if(!cfg.existingProxy){
32261         /** @private */
32262         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32263     }else{
32264         this.proxy = Roo.get(cfg.existingProxy).dom;
32265     }
32266     /** @private */
32267     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32268     
32269     /** @private */
32270     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32271     
32272     /** @private */
32273     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32274     
32275     /** @private */
32276     this.dragSpecs = {};
32277     
32278     /**
32279      * @private The adapter to use to positon and resize elements
32280      */
32281     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32282     this.adapter.init(this);
32283     
32284     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32285         /** @private */
32286         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32287         this.el.addClass("roo-splitbar-h");
32288     }else{
32289         /** @private */
32290         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32291         this.el.addClass("roo-splitbar-v");
32292     }
32293     
32294     this.addEvents({
32295         /**
32296          * @event resize
32297          * Fires when the splitter is moved (alias for {@link #event-moved})
32298          * @param {Roo.bootstrap.SplitBar} this
32299          * @param {Number} newSize the new width or height
32300          */
32301         "resize" : true,
32302         /**
32303          * @event moved
32304          * Fires when the splitter is moved
32305          * @param {Roo.bootstrap.SplitBar} this
32306          * @param {Number} newSize the new width or height
32307          */
32308         "moved" : true,
32309         /**
32310          * @event beforeresize
32311          * Fires before the splitter is dragged
32312          * @param {Roo.bootstrap.SplitBar} this
32313          */
32314         "beforeresize" : true,
32315
32316         "beforeapply" : true
32317     });
32318
32319     Roo.util.Observable.call(this);
32320 };
32321
32322 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32323     onStartProxyDrag : function(x, y){
32324         this.fireEvent("beforeresize", this);
32325         if(!this.overlay){
32326             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32327             o.unselectable();
32328             o.enableDisplayMode("block");
32329             // all splitbars share the same overlay
32330             Roo.bootstrap.SplitBar.prototype.overlay = o;
32331         }
32332         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32333         this.overlay.show();
32334         Roo.get(this.proxy).setDisplayed("block");
32335         var size = this.adapter.getElementSize(this);
32336         this.activeMinSize = this.getMinimumSize();;
32337         this.activeMaxSize = this.getMaximumSize();;
32338         var c1 = size - this.activeMinSize;
32339         var c2 = Math.max(this.activeMaxSize - size, 0);
32340         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32341             this.dd.resetConstraints();
32342             this.dd.setXConstraint(
32343                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32344                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32345             );
32346             this.dd.setYConstraint(0, 0);
32347         }else{
32348             this.dd.resetConstraints();
32349             this.dd.setXConstraint(0, 0);
32350             this.dd.setYConstraint(
32351                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32352                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32353             );
32354          }
32355         this.dragSpecs.startSize = size;
32356         this.dragSpecs.startPoint = [x, y];
32357         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32358     },
32359     
32360     /** 
32361      * @private Called after the drag operation by the DDProxy
32362      */
32363     onEndProxyDrag : function(e){
32364         Roo.get(this.proxy).setDisplayed(false);
32365         var endPoint = Roo.lib.Event.getXY(e);
32366         if(this.overlay){
32367             this.overlay.hide();
32368         }
32369         var newSize;
32370         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32371             newSize = this.dragSpecs.startSize + 
32372                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32373                     endPoint[0] - this.dragSpecs.startPoint[0] :
32374                     this.dragSpecs.startPoint[0] - endPoint[0]
32375                 );
32376         }else{
32377             newSize = this.dragSpecs.startSize + 
32378                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32379                     endPoint[1] - this.dragSpecs.startPoint[1] :
32380                     this.dragSpecs.startPoint[1] - endPoint[1]
32381                 );
32382         }
32383         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32384         if(newSize != this.dragSpecs.startSize){
32385             if(this.fireEvent('beforeapply', this, newSize) !== false){
32386                 this.adapter.setElementSize(this, newSize);
32387                 this.fireEvent("moved", this, newSize);
32388                 this.fireEvent("resize", this, newSize);
32389             }
32390         }
32391     },
32392     
32393     /**
32394      * Get the adapter this SplitBar uses
32395      * @return The adapter object
32396      */
32397     getAdapter : function(){
32398         return this.adapter;
32399     },
32400     
32401     /**
32402      * Set the adapter this SplitBar uses
32403      * @param {Object} adapter A SplitBar adapter object
32404      */
32405     setAdapter : function(adapter){
32406         this.adapter = adapter;
32407         this.adapter.init(this);
32408     },
32409     
32410     /**
32411      * Gets the minimum size for the resizing element
32412      * @return {Number} The minimum size
32413      */
32414     getMinimumSize : function(){
32415         return this.minSize;
32416     },
32417     
32418     /**
32419      * Sets the minimum size for the resizing element
32420      * @param {Number} minSize The minimum size
32421      */
32422     setMinimumSize : function(minSize){
32423         this.minSize = minSize;
32424     },
32425     
32426     /**
32427      * Gets the maximum size for the resizing element
32428      * @return {Number} The maximum size
32429      */
32430     getMaximumSize : function(){
32431         return this.maxSize;
32432     },
32433     
32434     /**
32435      * Sets the maximum size for the resizing element
32436      * @param {Number} maxSize The maximum size
32437      */
32438     setMaximumSize : function(maxSize){
32439         this.maxSize = maxSize;
32440     },
32441     
32442     /**
32443      * Sets the initialize size for the resizing element
32444      * @param {Number} size The initial size
32445      */
32446     setCurrentSize : function(size){
32447         var oldAnimate = this.animate;
32448         this.animate = false;
32449         this.adapter.setElementSize(this, size);
32450         this.animate = oldAnimate;
32451     },
32452     
32453     /**
32454      * Destroy this splitbar. 
32455      * @param {Boolean} removeEl True to remove the element
32456      */
32457     destroy : function(removeEl){
32458         if(this.shim){
32459             this.shim.remove();
32460         }
32461         this.dd.unreg();
32462         this.proxy.parentNode.removeChild(this.proxy);
32463         if(removeEl){
32464             this.el.remove();
32465         }
32466     }
32467 });
32468
32469 /**
32470  * @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.
32471  */
32472 Roo.bootstrap.SplitBar.createProxy = function(dir){
32473     var proxy = new Roo.Element(document.createElement("div"));
32474     proxy.unselectable();
32475     var cls = 'roo-splitbar-proxy';
32476     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32477     document.body.appendChild(proxy.dom);
32478     return proxy.dom;
32479 };
32480
32481 /** 
32482  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32483  * Default Adapter. It assumes the splitter and resizing element are not positioned
32484  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32485  */
32486 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32487 };
32488
32489 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32490     // do nothing for now
32491     init : function(s){
32492     
32493     },
32494     /**
32495      * Called before drag operations to get the current size of the resizing element. 
32496      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32497      */
32498      getElementSize : function(s){
32499         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32500             return s.resizingEl.getWidth();
32501         }else{
32502             return s.resizingEl.getHeight();
32503         }
32504     },
32505     
32506     /**
32507      * Called after drag operations to set the size of the resizing element.
32508      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32509      * @param {Number} newSize The new size to set
32510      * @param {Function} onComplete A function to be invoked when resizing is complete
32511      */
32512     setElementSize : function(s, newSize, onComplete){
32513         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32514             if(!s.animate){
32515                 s.resizingEl.setWidth(newSize);
32516                 if(onComplete){
32517                     onComplete(s, newSize);
32518                 }
32519             }else{
32520                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32521             }
32522         }else{
32523             
32524             if(!s.animate){
32525                 s.resizingEl.setHeight(newSize);
32526                 if(onComplete){
32527                     onComplete(s, newSize);
32528                 }
32529             }else{
32530                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32531             }
32532         }
32533     }
32534 };
32535
32536 /** 
32537  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32538  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32539  * Adapter that  moves the splitter element to align with the resized sizing element. 
32540  * Used with an absolute positioned SplitBar.
32541  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32542  * document.body, make sure you assign an id to the body element.
32543  */
32544 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32545     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32546     this.container = Roo.get(container);
32547 };
32548
32549 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32550     init : function(s){
32551         this.basic.init(s);
32552     },
32553     
32554     getElementSize : function(s){
32555         return this.basic.getElementSize(s);
32556     },
32557     
32558     setElementSize : function(s, newSize, onComplete){
32559         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32560     },
32561     
32562     moveSplitter : function(s){
32563         var yes = Roo.bootstrap.SplitBar;
32564         switch(s.placement){
32565             case yes.LEFT:
32566                 s.el.setX(s.resizingEl.getRight());
32567                 break;
32568             case yes.RIGHT:
32569                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32570                 break;
32571             case yes.TOP:
32572                 s.el.setY(s.resizingEl.getBottom());
32573                 break;
32574             case yes.BOTTOM:
32575                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32576                 break;
32577         }
32578     }
32579 };
32580
32581 /**
32582  * Orientation constant - Create a vertical SplitBar
32583  * @static
32584  * @type Number
32585  */
32586 Roo.bootstrap.SplitBar.VERTICAL = 1;
32587
32588 /**
32589  * Orientation constant - Create a horizontal SplitBar
32590  * @static
32591  * @type Number
32592  */
32593 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32594
32595 /**
32596  * Placement constant - The resizing element is to the left of the splitter element
32597  * @static
32598  * @type Number
32599  */
32600 Roo.bootstrap.SplitBar.LEFT = 1;
32601
32602 /**
32603  * Placement constant - The resizing element is to the right of the splitter element
32604  * @static
32605  * @type Number
32606  */
32607 Roo.bootstrap.SplitBar.RIGHT = 2;
32608
32609 /**
32610  * Placement constant - The resizing element is positioned above the splitter element
32611  * @static
32612  * @type Number
32613  */
32614 Roo.bootstrap.SplitBar.TOP = 3;
32615
32616 /**
32617  * Placement constant - The resizing element is positioned under splitter element
32618  * @static
32619  * @type Number
32620  */
32621 Roo.bootstrap.SplitBar.BOTTOM = 4;
32622 Roo.namespace("Roo.bootstrap.layout");/*
32623  * Based on:
32624  * Ext JS Library 1.1.1
32625  * Copyright(c) 2006-2007, Ext JS, LLC.
32626  *
32627  * Originally Released Under LGPL - original licence link has changed is not relivant.
32628  *
32629  * Fork - LGPL
32630  * <script type="text/javascript">
32631  */
32632
32633 /**
32634  * @class Roo.bootstrap.layout.Manager
32635  * @extends Roo.bootstrap.Component
32636  * Base class for layout managers.
32637  */
32638 Roo.bootstrap.layout.Manager = function(config)
32639 {
32640     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32641
32642
32643
32644
32645
32646     /** false to disable window resize monitoring @type Boolean */
32647     this.monitorWindowResize = true;
32648     this.regions = {};
32649     this.addEvents({
32650         /**
32651          * @event layout
32652          * Fires when a layout is performed.
32653          * @param {Roo.LayoutManager} this
32654          */
32655         "layout" : true,
32656         /**
32657          * @event regionresized
32658          * Fires when the user resizes a region.
32659          * @param {Roo.LayoutRegion} region The resized region
32660          * @param {Number} newSize The new size (width for east/west, height for north/south)
32661          */
32662         "regionresized" : true,
32663         /**
32664          * @event regioncollapsed
32665          * Fires when a region is collapsed.
32666          * @param {Roo.LayoutRegion} region The collapsed region
32667          */
32668         "regioncollapsed" : true,
32669         /**
32670          * @event regionexpanded
32671          * Fires when a region is expanded.
32672          * @param {Roo.LayoutRegion} region The expanded region
32673          */
32674         "regionexpanded" : true
32675     });
32676     this.updating = false;
32677
32678     if (config.el) {
32679         this.el = Roo.get(config.el);
32680         this.initEvents();
32681     }
32682
32683 };
32684
32685 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32686
32687
32688     regions : null,
32689
32690     monitorWindowResize : true,
32691
32692
32693     updating : false,
32694
32695
32696     onRender : function(ct, position)
32697     {
32698         if(!this.el){
32699             this.el = Roo.get(ct);
32700             this.initEvents();
32701         }
32702         //this.fireEvent('render',this);
32703     },
32704
32705
32706     initEvents: function()
32707     {
32708
32709
32710         // ie scrollbar fix
32711         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32712             document.body.scroll = "no";
32713         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32714             this.el.position('relative');
32715         }
32716         this.id = this.el.id;
32717         this.el.addClass("roo-layout-container");
32718         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32719         if(this.el.dom != document.body ) {
32720             this.el.on('resize', this.layout,this);
32721             this.el.on('show', this.layout,this);
32722         }
32723
32724     },
32725
32726     /**
32727      * Returns true if this layout is currently being updated
32728      * @return {Boolean}
32729      */
32730     isUpdating : function(){
32731         return this.updating;
32732     },
32733
32734     /**
32735      * Suspend the LayoutManager from doing auto-layouts while
32736      * making multiple add or remove calls
32737      */
32738     beginUpdate : function(){
32739         this.updating = true;
32740     },
32741
32742     /**
32743      * Restore auto-layouts and optionally disable the manager from performing a layout
32744      * @param {Boolean} noLayout true to disable a layout update
32745      */
32746     endUpdate : function(noLayout){
32747         this.updating = false;
32748         if(!noLayout){
32749             this.layout();
32750         }
32751     },
32752
32753     layout: function(){
32754         // abstract...
32755     },
32756
32757     onRegionResized : function(region, newSize){
32758         this.fireEvent("regionresized", region, newSize);
32759         this.layout();
32760     },
32761
32762     onRegionCollapsed : function(region){
32763         this.fireEvent("regioncollapsed", region);
32764     },
32765
32766     onRegionExpanded : function(region){
32767         this.fireEvent("regionexpanded", region);
32768     },
32769
32770     /**
32771      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32772      * performs box-model adjustments.
32773      * @return {Object} The size as an object {width: (the width), height: (the height)}
32774      */
32775     getViewSize : function()
32776     {
32777         var size;
32778         if(this.el.dom != document.body){
32779             size = this.el.getSize();
32780         }else{
32781             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32782         }
32783         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32784         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32785         return size;
32786     },
32787
32788     /**
32789      * Returns the Element this layout is bound to.
32790      * @return {Roo.Element}
32791      */
32792     getEl : function(){
32793         return this.el;
32794     },
32795
32796     /**
32797      * Returns the specified region.
32798      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32799      * @return {Roo.LayoutRegion}
32800      */
32801     getRegion : function(target){
32802         return this.regions[target.toLowerCase()];
32803     },
32804
32805     onWindowResize : function(){
32806         if(this.monitorWindowResize){
32807             this.layout();
32808         }
32809     }
32810 });
32811 /*
32812  * Based on:
32813  * Ext JS Library 1.1.1
32814  * Copyright(c) 2006-2007, Ext JS, LLC.
32815  *
32816  * Originally Released Under LGPL - original licence link has changed is not relivant.
32817  *
32818  * Fork - LGPL
32819  * <script type="text/javascript">
32820  */
32821 /**
32822  * @class Roo.bootstrap.layout.Border
32823  * @extends Roo.bootstrap.layout.Manager
32824  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32825  * please see: examples/bootstrap/nested.html<br><br>
32826  
32827 <b>The container the layout is rendered into can be either the body element or any other element.
32828 If it is not the body element, the container needs to either be an absolute positioned element,
32829 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32830 the container size if it is not the body element.</b>
32831
32832 * @constructor
32833 * Create a new Border
32834 * @param {Object} config Configuration options
32835  */
32836 Roo.bootstrap.layout.Border = function(config){
32837     config = config || {};
32838     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32839     
32840     
32841     
32842     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32843         if(config[region]){
32844             config[region].region = region;
32845             this.addRegion(config[region]);
32846         }
32847     },this);
32848     
32849 };
32850
32851 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32852
32853 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32854     /**
32855      * Creates and adds a new region if it doesn't already exist.
32856      * @param {String} target The target region key (north, south, east, west or center).
32857      * @param {Object} config The regions config object
32858      * @return {BorderLayoutRegion} The new region
32859      */
32860     addRegion : function(config)
32861     {
32862         if(!this.regions[config.region]){
32863             var r = this.factory(config);
32864             this.bindRegion(r);
32865         }
32866         return this.regions[config.region];
32867     },
32868
32869     // private (kinda)
32870     bindRegion : function(r){
32871         this.regions[r.config.region] = r;
32872         
32873         r.on("visibilitychange",    this.layout, this);
32874         r.on("paneladded",          this.layout, this);
32875         r.on("panelremoved",        this.layout, this);
32876         r.on("invalidated",         this.layout, this);
32877         r.on("resized",             this.onRegionResized, this);
32878         r.on("collapsed",           this.onRegionCollapsed, this);
32879         r.on("expanded",            this.onRegionExpanded, this);
32880     },
32881
32882     /**
32883      * Performs a layout update.
32884      */
32885     layout : function()
32886     {
32887         if(this.updating) {
32888             return;
32889         }
32890         
32891         // render all the rebions if they have not been done alreayd?
32892         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32893             if(this.regions[region] && !this.regions[region].bodyEl){
32894                 this.regions[region].onRender(this.el)
32895             }
32896         },this);
32897         
32898         var size = this.getViewSize();
32899         var w = size.width;
32900         var h = size.height;
32901         var centerW = w;
32902         var centerH = h;
32903         var centerY = 0;
32904         var centerX = 0;
32905         //var x = 0, y = 0;
32906
32907         var rs = this.regions;
32908         var north = rs["north"];
32909         var south = rs["south"]; 
32910         var west = rs["west"];
32911         var east = rs["east"];
32912         var center = rs["center"];
32913         //if(this.hideOnLayout){ // not supported anymore
32914             //c.el.setStyle("display", "none");
32915         //}
32916         if(north && north.isVisible()){
32917             var b = north.getBox();
32918             var m = north.getMargins();
32919             b.width = w - (m.left+m.right);
32920             b.x = m.left;
32921             b.y = m.top;
32922             centerY = b.height + b.y + m.bottom;
32923             centerH -= centerY;
32924             north.updateBox(this.safeBox(b));
32925         }
32926         if(south && south.isVisible()){
32927             var b = south.getBox();
32928             var m = south.getMargins();
32929             b.width = w - (m.left+m.right);
32930             b.x = m.left;
32931             var totalHeight = (b.height + m.top + m.bottom);
32932             b.y = h - totalHeight + m.top;
32933             centerH -= totalHeight;
32934             south.updateBox(this.safeBox(b));
32935         }
32936         if(west && west.isVisible()){
32937             var b = west.getBox();
32938             var m = west.getMargins();
32939             b.height = centerH - (m.top+m.bottom);
32940             b.x = m.left;
32941             b.y = centerY + m.top;
32942             var totalWidth = (b.width + m.left + m.right);
32943             centerX += totalWidth;
32944             centerW -= totalWidth;
32945             west.updateBox(this.safeBox(b));
32946         }
32947         if(east && east.isVisible()){
32948             var b = east.getBox();
32949             var m = east.getMargins();
32950             b.height = centerH - (m.top+m.bottom);
32951             var totalWidth = (b.width + m.left + m.right);
32952             b.x = w - totalWidth + m.left;
32953             b.y = centerY + m.top;
32954             centerW -= totalWidth;
32955             east.updateBox(this.safeBox(b));
32956         }
32957         if(center){
32958             var m = center.getMargins();
32959             var centerBox = {
32960                 x: centerX + m.left,
32961                 y: centerY + m.top,
32962                 width: centerW - (m.left+m.right),
32963                 height: centerH - (m.top+m.bottom)
32964             };
32965             //if(this.hideOnLayout){
32966                 //center.el.setStyle("display", "block");
32967             //}
32968             center.updateBox(this.safeBox(centerBox));
32969         }
32970         this.el.repaint();
32971         this.fireEvent("layout", this);
32972     },
32973
32974     // private
32975     safeBox : function(box){
32976         box.width = Math.max(0, box.width);
32977         box.height = Math.max(0, box.height);
32978         return box;
32979     },
32980
32981     /**
32982      * Adds a ContentPanel (or subclass) to this layout.
32983      * @param {String} target The target region key (north, south, east, west or center).
32984      * @param {Roo.ContentPanel} panel The panel to add
32985      * @return {Roo.ContentPanel} The added panel
32986      */
32987     add : function(target, panel){
32988          
32989         target = target.toLowerCase();
32990         return this.regions[target].add(panel);
32991     },
32992
32993     /**
32994      * Remove a ContentPanel (or subclass) to this layout.
32995      * @param {String} target The target region key (north, south, east, west or center).
32996      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32997      * @return {Roo.ContentPanel} The removed panel
32998      */
32999     remove : function(target, panel){
33000         target = target.toLowerCase();
33001         return this.regions[target].remove(panel);
33002     },
33003
33004     /**
33005      * Searches all regions for a panel with the specified id
33006      * @param {String} panelId
33007      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33008      */
33009     findPanel : function(panelId){
33010         var rs = this.regions;
33011         for(var target in rs){
33012             if(typeof rs[target] != "function"){
33013                 var p = rs[target].getPanel(panelId);
33014                 if(p){
33015                     return p;
33016                 }
33017             }
33018         }
33019         return null;
33020     },
33021
33022     /**
33023      * Searches all regions for a panel with the specified id and activates (shows) it.
33024      * @param {String/ContentPanel} panelId The panels id or the panel itself
33025      * @return {Roo.ContentPanel} The shown panel or null
33026      */
33027     showPanel : function(panelId) {
33028       var rs = this.regions;
33029       for(var target in rs){
33030          var r = rs[target];
33031          if(typeof r != "function"){
33032             if(r.hasPanel(panelId)){
33033                return r.showPanel(panelId);
33034             }
33035          }
33036       }
33037       return null;
33038    },
33039
33040    /**
33041      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33042      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33043      */
33044    /*
33045     restoreState : function(provider){
33046         if(!provider){
33047             provider = Roo.state.Manager;
33048         }
33049         var sm = new Roo.LayoutStateManager();
33050         sm.init(this, provider);
33051     },
33052 */
33053  
33054  
33055     /**
33056      * Adds a xtype elements to the layout.
33057      * <pre><code>
33058
33059 layout.addxtype({
33060        xtype : 'ContentPanel',
33061        region: 'west',
33062        items: [ .... ]
33063    }
33064 );
33065
33066 layout.addxtype({
33067         xtype : 'NestedLayoutPanel',
33068         region: 'west',
33069         layout: {
33070            center: { },
33071            west: { }   
33072         },
33073         items : [ ... list of content panels or nested layout panels.. ]
33074    }
33075 );
33076 </code></pre>
33077      * @param {Object} cfg Xtype definition of item to add.
33078      */
33079     addxtype : function(cfg)
33080     {
33081         // basically accepts a pannel...
33082         // can accept a layout region..!?!?
33083         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33084         
33085         
33086         // theory?  children can only be panels??
33087         
33088         //if (!cfg.xtype.match(/Panel$/)) {
33089         //    return false;
33090         //}
33091         var ret = false;
33092         
33093         if (typeof(cfg.region) == 'undefined') {
33094             Roo.log("Failed to add Panel, region was not set");
33095             Roo.log(cfg);
33096             return false;
33097         }
33098         var region = cfg.region;
33099         delete cfg.region;
33100         
33101           
33102         var xitems = [];
33103         if (cfg.items) {
33104             xitems = cfg.items;
33105             delete cfg.items;
33106         }
33107         var nb = false;
33108         
33109         switch(cfg.xtype) 
33110         {
33111             case 'Content':  // ContentPanel (el, cfg)
33112             case 'Scroll':  // ContentPanel (el, cfg)
33113             case 'View': 
33114                 cfg.autoCreate = true;
33115                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33116                 //} else {
33117                 //    var el = this.el.createChild();
33118                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33119                 //}
33120                 
33121                 this.add(region, ret);
33122                 break;
33123             
33124             /*
33125             case 'TreePanel': // our new panel!
33126                 cfg.el = this.el.createChild();
33127                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33128                 this.add(region, ret);
33129                 break;
33130             */
33131             
33132             case 'Nest': 
33133                 // create a new Layout (which is  a Border Layout...
33134                 
33135                 var clayout = cfg.layout;
33136                 clayout.el  = this.el.createChild();
33137                 clayout.items   = clayout.items  || [];
33138                 
33139                 delete cfg.layout;
33140                 
33141                 // replace this exitems with the clayout ones..
33142                 xitems = clayout.items;
33143                  
33144                 // force background off if it's in center...
33145                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33146                     cfg.background = false;
33147                 }
33148                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33149                 
33150                 
33151                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33152                 //console.log('adding nested layout panel '  + cfg.toSource());
33153                 this.add(region, ret);
33154                 nb = {}; /// find first...
33155                 break;
33156             
33157             case 'Grid':
33158                 
33159                 // needs grid and region
33160                 
33161                 //var el = this.getRegion(region).el.createChild();
33162                 /*
33163                  *var el = this.el.createChild();
33164                 // create the grid first...
33165                 cfg.grid.container = el;
33166                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33167                 */
33168                 
33169                 if (region == 'center' && this.active ) {
33170                     cfg.background = false;
33171                 }
33172                 
33173                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33174                 
33175                 this.add(region, ret);
33176                 /*
33177                 if (cfg.background) {
33178                     // render grid on panel activation (if panel background)
33179                     ret.on('activate', function(gp) {
33180                         if (!gp.grid.rendered) {
33181                     //        gp.grid.render(el);
33182                         }
33183                     });
33184                 } else {
33185                   //  cfg.grid.render(el);
33186                 }
33187                 */
33188                 break;
33189            
33190            
33191             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33192                 // it was the old xcomponent building that caused this before.
33193                 // espeically if border is the top element in the tree.
33194                 ret = this;
33195                 break; 
33196                 
33197                     
33198                 
33199                 
33200                 
33201             default:
33202                 /*
33203                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33204                     
33205                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33206                     this.add(region, ret);
33207                 } else {
33208                 */
33209                     Roo.log(cfg);
33210                     throw "Can not add '" + cfg.xtype + "' to Border";
33211                     return null;
33212              
33213                                 
33214              
33215         }
33216         this.beginUpdate();
33217         // add children..
33218         var region = '';
33219         var abn = {};
33220         Roo.each(xitems, function(i)  {
33221             region = nb && i.region ? i.region : false;
33222             
33223             var add = ret.addxtype(i);
33224            
33225             if (region) {
33226                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33227                 if (!i.background) {
33228                     abn[region] = nb[region] ;
33229                 }
33230             }
33231             
33232         });
33233         this.endUpdate();
33234
33235         // make the last non-background panel active..
33236         //if (nb) { Roo.log(abn); }
33237         if (nb) {
33238             
33239             for(var r in abn) {
33240                 region = this.getRegion(r);
33241                 if (region) {
33242                     // tried using nb[r], but it does not work..
33243                      
33244                     region.showPanel(abn[r]);
33245                    
33246                 }
33247             }
33248         }
33249         return ret;
33250         
33251     },
33252     
33253     
33254 // private
33255     factory : function(cfg)
33256     {
33257         
33258         var validRegions = Roo.bootstrap.layout.Border.regions;
33259
33260         var target = cfg.region;
33261         cfg.mgr = this;
33262         
33263         var r = Roo.bootstrap.layout;
33264         Roo.log(target);
33265         switch(target){
33266             case "north":
33267                 return new r.North(cfg);
33268             case "south":
33269                 return new r.South(cfg);
33270             case "east":
33271                 return new r.East(cfg);
33272             case "west":
33273                 return new r.West(cfg);
33274             case "center":
33275                 return new r.Center(cfg);
33276         }
33277         throw 'Layout region "'+target+'" not supported.';
33278     }
33279     
33280     
33281 });
33282  /*
33283  * Based on:
33284  * Ext JS Library 1.1.1
33285  * Copyright(c) 2006-2007, Ext JS, LLC.
33286  *
33287  * Originally Released Under LGPL - original licence link has changed is not relivant.
33288  *
33289  * Fork - LGPL
33290  * <script type="text/javascript">
33291  */
33292  
33293 /**
33294  * @class Roo.bootstrap.layout.Basic
33295  * @extends Roo.util.Observable
33296  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33297  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33298  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33299  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33300  * @cfg {string}   region  the region that it inhabits..
33301  * @cfg {bool}   skipConfig skip config?
33302  * 
33303
33304  */
33305 Roo.bootstrap.layout.Basic = function(config){
33306     
33307     this.mgr = config.mgr;
33308     
33309     this.position = config.region;
33310     
33311     var skipConfig = config.skipConfig;
33312     
33313     this.events = {
33314         /**
33315          * @scope Roo.BasicLayoutRegion
33316          */
33317         
33318         /**
33319          * @event beforeremove
33320          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33321          * @param {Roo.LayoutRegion} this
33322          * @param {Roo.ContentPanel} panel The panel
33323          * @param {Object} e The cancel event object
33324          */
33325         "beforeremove" : true,
33326         /**
33327          * @event invalidated
33328          * Fires when the layout for this region is changed.
33329          * @param {Roo.LayoutRegion} this
33330          */
33331         "invalidated" : true,
33332         /**
33333          * @event visibilitychange
33334          * Fires when this region is shown or hidden 
33335          * @param {Roo.LayoutRegion} this
33336          * @param {Boolean} visibility true or false
33337          */
33338         "visibilitychange" : true,
33339         /**
33340          * @event paneladded
33341          * Fires when a panel is added. 
33342          * @param {Roo.LayoutRegion} this
33343          * @param {Roo.ContentPanel} panel The panel
33344          */
33345         "paneladded" : true,
33346         /**
33347          * @event panelremoved
33348          * Fires when a panel is removed. 
33349          * @param {Roo.LayoutRegion} this
33350          * @param {Roo.ContentPanel} panel The panel
33351          */
33352         "panelremoved" : true,
33353         /**
33354          * @event beforecollapse
33355          * Fires when this region before collapse.
33356          * @param {Roo.LayoutRegion} this
33357          */
33358         "beforecollapse" : true,
33359         /**
33360          * @event collapsed
33361          * Fires when this region is collapsed.
33362          * @param {Roo.LayoutRegion} this
33363          */
33364         "collapsed" : true,
33365         /**
33366          * @event expanded
33367          * Fires when this region is expanded.
33368          * @param {Roo.LayoutRegion} this
33369          */
33370         "expanded" : true,
33371         /**
33372          * @event slideshow
33373          * Fires when this region is slid into view.
33374          * @param {Roo.LayoutRegion} this
33375          */
33376         "slideshow" : true,
33377         /**
33378          * @event slidehide
33379          * Fires when this region slides out of view. 
33380          * @param {Roo.LayoutRegion} this
33381          */
33382         "slidehide" : true,
33383         /**
33384          * @event panelactivated
33385          * Fires when a panel is activated. 
33386          * @param {Roo.LayoutRegion} this
33387          * @param {Roo.ContentPanel} panel The activated panel
33388          */
33389         "panelactivated" : true,
33390         /**
33391          * @event resized
33392          * Fires when the user resizes this region. 
33393          * @param {Roo.LayoutRegion} this
33394          * @param {Number} newSize The new size (width for east/west, height for north/south)
33395          */
33396         "resized" : true
33397     };
33398     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33399     this.panels = new Roo.util.MixedCollection();
33400     this.panels.getKey = this.getPanelId.createDelegate(this);
33401     this.box = null;
33402     this.activePanel = null;
33403     // ensure listeners are added...
33404     
33405     if (config.listeners || config.events) {
33406         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33407             listeners : config.listeners || {},
33408             events : config.events || {}
33409         });
33410     }
33411     
33412     if(skipConfig !== true){
33413         this.applyConfig(config);
33414     }
33415 };
33416
33417 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33418 {
33419     getPanelId : function(p){
33420         return p.getId();
33421     },
33422     
33423     applyConfig : function(config){
33424         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33425         this.config = config;
33426         
33427     },
33428     
33429     /**
33430      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33431      * the width, for horizontal (north, south) the height.
33432      * @param {Number} newSize The new width or height
33433      */
33434     resizeTo : function(newSize){
33435         var el = this.el ? this.el :
33436                  (this.activePanel ? this.activePanel.getEl() : null);
33437         if(el){
33438             switch(this.position){
33439                 case "east":
33440                 case "west":
33441                     el.setWidth(newSize);
33442                     this.fireEvent("resized", this, newSize);
33443                 break;
33444                 case "north":
33445                 case "south":
33446                     el.setHeight(newSize);
33447                     this.fireEvent("resized", this, newSize);
33448                 break;                
33449             }
33450         }
33451     },
33452     
33453     getBox : function(){
33454         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33455     },
33456     
33457     getMargins : function(){
33458         return this.margins;
33459     },
33460     
33461     updateBox : function(box){
33462         this.box = box;
33463         var el = this.activePanel.getEl();
33464         el.dom.style.left = box.x + "px";
33465         el.dom.style.top = box.y + "px";
33466         this.activePanel.setSize(box.width, box.height);
33467     },
33468     
33469     /**
33470      * Returns the container element for this region.
33471      * @return {Roo.Element}
33472      */
33473     getEl : function(){
33474         return this.activePanel;
33475     },
33476     
33477     /**
33478      * Returns true if this region is currently visible.
33479      * @return {Boolean}
33480      */
33481     isVisible : function(){
33482         return this.activePanel ? true : false;
33483     },
33484     
33485     setActivePanel : function(panel){
33486         panel = this.getPanel(panel);
33487         if(this.activePanel && this.activePanel != panel){
33488             this.activePanel.setActiveState(false);
33489             this.activePanel.getEl().setLeftTop(-10000,-10000);
33490         }
33491         this.activePanel = panel;
33492         panel.setActiveState(true);
33493         if(this.box){
33494             panel.setSize(this.box.width, this.box.height);
33495         }
33496         this.fireEvent("panelactivated", this, panel);
33497         this.fireEvent("invalidated");
33498     },
33499     
33500     /**
33501      * Show the specified panel.
33502      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33503      * @return {Roo.ContentPanel} The shown panel or null
33504      */
33505     showPanel : function(panel){
33506         panel = this.getPanel(panel);
33507         if(panel){
33508             this.setActivePanel(panel);
33509         }
33510         return panel;
33511     },
33512     
33513     /**
33514      * Get the active panel for this region.
33515      * @return {Roo.ContentPanel} The active panel or null
33516      */
33517     getActivePanel : function(){
33518         return this.activePanel;
33519     },
33520     
33521     /**
33522      * Add the passed ContentPanel(s)
33523      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33524      * @return {Roo.ContentPanel} The panel added (if only one was added)
33525      */
33526     add : function(panel){
33527         if(arguments.length > 1){
33528             for(var i = 0, len = arguments.length; i < len; i++) {
33529                 this.add(arguments[i]);
33530             }
33531             return null;
33532         }
33533         if(this.hasPanel(panel)){
33534             this.showPanel(panel);
33535             return panel;
33536         }
33537         var el = panel.getEl();
33538         if(el.dom.parentNode != this.mgr.el.dom){
33539             this.mgr.el.dom.appendChild(el.dom);
33540         }
33541         if(panel.setRegion){
33542             panel.setRegion(this);
33543         }
33544         this.panels.add(panel);
33545         el.setStyle("position", "absolute");
33546         if(!panel.background){
33547             this.setActivePanel(panel);
33548             if(this.config.initialSize && this.panels.getCount()==1){
33549                 this.resizeTo(this.config.initialSize);
33550             }
33551         }
33552         this.fireEvent("paneladded", this, panel);
33553         return panel;
33554     },
33555     
33556     /**
33557      * Returns true if the panel is in this region.
33558      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33559      * @return {Boolean}
33560      */
33561     hasPanel : function(panel){
33562         if(typeof panel == "object"){ // must be panel obj
33563             panel = panel.getId();
33564         }
33565         return this.getPanel(panel) ? true : false;
33566     },
33567     
33568     /**
33569      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33570      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33571      * @param {Boolean} preservePanel Overrides the config preservePanel option
33572      * @return {Roo.ContentPanel} The panel that was removed
33573      */
33574     remove : function(panel, preservePanel){
33575         panel = this.getPanel(panel);
33576         if(!panel){
33577             return null;
33578         }
33579         var e = {};
33580         this.fireEvent("beforeremove", this, panel, e);
33581         if(e.cancel === true){
33582             return null;
33583         }
33584         var panelId = panel.getId();
33585         this.panels.removeKey(panelId);
33586         return panel;
33587     },
33588     
33589     /**
33590      * Returns the panel specified or null if it's not in this region.
33591      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33592      * @return {Roo.ContentPanel}
33593      */
33594     getPanel : function(id){
33595         if(typeof id == "object"){ // must be panel obj
33596             return id;
33597         }
33598         return this.panels.get(id);
33599     },
33600     
33601     /**
33602      * Returns this regions position (north/south/east/west/center).
33603      * @return {String} 
33604      */
33605     getPosition: function(){
33606         return this.position;    
33607     }
33608 });/*
33609  * Based on:
33610  * Ext JS Library 1.1.1
33611  * Copyright(c) 2006-2007, Ext JS, LLC.
33612  *
33613  * Originally Released Under LGPL - original licence link has changed is not relivant.
33614  *
33615  * Fork - LGPL
33616  * <script type="text/javascript">
33617  */
33618  
33619 /**
33620  * @class Roo.bootstrap.layout.Region
33621  * @extends Roo.bootstrap.layout.Basic
33622  * This class represents a region in a layout manager.
33623  
33624  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33625  * @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})
33626  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33627  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33628  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33629  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33630  * @cfg {String}    title           The title for the region (overrides panel titles)
33631  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33632  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33633  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33634  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33635  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33636  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33637  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33638  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33639  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33640  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33641
33642  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33643  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33644  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33645  * @cfg {Number}    width           For East/West panels
33646  * @cfg {Number}    height          For North/South panels
33647  * @cfg {Boolean}   split           To show the splitter
33648  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33649  * 
33650  * @cfg {string}   cls             Extra CSS classes to add to region
33651  * 
33652  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33653  * @cfg {string}   region  the region that it inhabits..
33654  *
33655
33656  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33657  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33658
33659  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33660  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33661  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33662  */
33663 Roo.bootstrap.layout.Region = function(config)
33664 {
33665     this.applyConfig(config);
33666
33667     var mgr = config.mgr;
33668     var pos = config.region;
33669     config.skipConfig = true;
33670     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33671     
33672     if (mgr.el) {
33673         this.onRender(mgr.el);   
33674     }
33675      
33676     this.visible = true;
33677     this.collapsed = false;
33678     this.unrendered_panels = [];
33679 };
33680
33681 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33682
33683     position: '', // set by wrapper (eg. north/south etc..)
33684     unrendered_panels : null,  // unrendered panels.
33685     createBody : function(){
33686         /** This region's body element 
33687         * @type Roo.Element */
33688         this.bodyEl = this.el.createChild({
33689                 tag: "div",
33690                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33691         });
33692     },
33693
33694     onRender: function(ctr, pos)
33695     {
33696         var dh = Roo.DomHelper;
33697         /** This region's container element 
33698         * @type Roo.Element */
33699         this.el = dh.append(ctr.dom, {
33700                 tag: "div",
33701                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33702             }, true);
33703         /** This region's title element 
33704         * @type Roo.Element */
33705     
33706         this.titleEl = dh.append(this.el.dom,
33707             {
33708                     tag: "div",
33709                     unselectable: "on",
33710                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33711                     children:[
33712                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33713                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33714                     ]}, true);
33715         
33716         this.titleEl.enableDisplayMode();
33717         /** This region's title text element 
33718         * @type HTMLElement */
33719         this.titleTextEl = this.titleEl.dom.firstChild;
33720         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33721         /*
33722         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33723         this.closeBtn.enableDisplayMode();
33724         this.closeBtn.on("click", this.closeClicked, this);
33725         this.closeBtn.hide();
33726     */
33727         this.createBody(this.config);
33728         if(this.config.hideWhenEmpty){
33729             this.hide();
33730             this.on("paneladded", this.validateVisibility, this);
33731             this.on("panelremoved", this.validateVisibility, this);
33732         }
33733         if(this.autoScroll){
33734             this.bodyEl.setStyle("overflow", "auto");
33735         }else{
33736             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33737         }
33738         //if(c.titlebar !== false){
33739             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33740                 this.titleEl.hide();
33741             }else{
33742                 this.titleEl.show();
33743                 if(this.config.title){
33744                     this.titleTextEl.innerHTML = this.config.title;
33745                 }
33746             }
33747         //}
33748         if(this.config.collapsed){
33749             this.collapse(true);
33750         }
33751         if(this.config.hidden){
33752             this.hide();
33753         }
33754         
33755         if (this.unrendered_panels && this.unrendered_panels.length) {
33756             for (var i =0;i< this.unrendered_panels.length; i++) {
33757                 this.add(this.unrendered_panels[i]);
33758             }
33759             this.unrendered_panels = null;
33760             
33761         }
33762         
33763     },
33764     
33765     applyConfig : function(c)
33766     {
33767         /*
33768          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33769             var dh = Roo.DomHelper;
33770             if(c.titlebar !== false){
33771                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33772                 this.collapseBtn.on("click", this.collapse, this);
33773                 this.collapseBtn.enableDisplayMode();
33774                 /*
33775                 if(c.showPin === true || this.showPin){
33776                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33777                     this.stickBtn.enableDisplayMode();
33778                     this.stickBtn.on("click", this.expand, this);
33779                     this.stickBtn.hide();
33780                 }
33781                 
33782             }
33783             */
33784             /** This region's collapsed element
33785             * @type Roo.Element */
33786             /*
33787              *
33788             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33789                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33790             ]}, true);
33791             
33792             if(c.floatable !== false){
33793                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33794                this.collapsedEl.on("click", this.collapseClick, this);
33795             }
33796
33797             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33798                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33799                    id: "message", unselectable: "on", style:{"float":"left"}});
33800                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33801              }
33802             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33803             this.expandBtn.on("click", this.expand, this);
33804             
33805         }
33806         
33807         if(this.collapseBtn){
33808             this.collapseBtn.setVisible(c.collapsible == true);
33809         }
33810         
33811         this.cmargins = c.cmargins || this.cmargins ||
33812                          (this.position == "west" || this.position == "east" ?
33813                              {top: 0, left: 2, right:2, bottom: 0} :
33814                              {top: 2, left: 0, right:0, bottom: 2});
33815         */
33816         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33817         
33818         
33819         this.bottomTabs = c.tabPosition != "top";
33820         
33821         this.autoScroll = c.autoScroll || false;
33822         
33823         
33824        
33825         
33826         this.duration = c.duration || .30;
33827         this.slideDuration = c.slideDuration || .45;
33828         this.config = c;
33829        
33830     },
33831     /**
33832      * Returns true if this region is currently visible.
33833      * @return {Boolean}
33834      */
33835     isVisible : function(){
33836         return this.visible;
33837     },
33838
33839     /**
33840      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33841      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33842      */
33843     //setCollapsedTitle : function(title){
33844     //    title = title || "&#160;";
33845      //   if(this.collapsedTitleTextEl){
33846       //      this.collapsedTitleTextEl.innerHTML = title;
33847        // }
33848     //},
33849
33850     getBox : function(){
33851         var b;
33852       //  if(!this.collapsed){
33853             b = this.el.getBox(false, true);
33854        // }else{
33855           //  b = this.collapsedEl.getBox(false, true);
33856         //}
33857         return b;
33858     },
33859
33860     getMargins : function(){
33861         return this.margins;
33862         //return this.collapsed ? this.cmargins : this.margins;
33863     },
33864 /*
33865     highlight : function(){
33866         this.el.addClass("x-layout-panel-dragover");
33867     },
33868
33869     unhighlight : function(){
33870         this.el.removeClass("x-layout-panel-dragover");
33871     },
33872 */
33873     updateBox : function(box)
33874     {
33875         if (!this.bodyEl) {
33876             return; // not rendered yet..
33877         }
33878         
33879         this.box = box;
33880         if(!this.collapsed){
33881             this.el.dom.style.left = box.x + "px";
33882             this.el.dom.style.top = box.y + "px";
33883             this.updateBody(box.width, box.height);
33884         }else{
33885             this.collapsedEl.dom.style.left = box.x + "px";
33886             this.collapsedEl.dom.style.top = box.y + "px";
33887             this.collapsedEl.setSize(box.width, box.height);
33888         }
33889         if(this.tabs){
33890             this.tabs.autoSizeTabs();
33891         }
33892     },
33893
33894     updateBody : function(w, h)
33895     {
33896         if(w !== null){
33897             this.el.setWidth(w);
33898             w -= this.el.getBorderWidth("rl");
33899             if(this.config.adjustments){
33900                 w += this.config.adjustments[0];
33901             }
33902         }
33903         if(h !== null && h > 0){
33904             this.el.setHeight(h);
33905             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33906             h -= this.el.getBorderWidth("tb");
33907             if(this.config.adjustments){
33908                 h += this.config.adjustments[1];
33909             }
33910             this.bodyEl.setHeight(h);
33911             if(this.tabs){
33912                 h = this.tabs.syncHeight(h);
33913             }
33914         }
33915         if(this.panelSize){
33916             w = w !== null ? w : this.panelSize.width;
33917             h = h !== null ? h : this.panelSize.height;
33918         }
33919         if(this.activePanel){
33920             var el = this.activePanel.getEl();
33921             w = w !== null ? w : el.getWidth();
33922             h = h !== null ? h : el.getHeight();
33923             this.panelSize = {width: w, height: h};
33924             this.activePanel.setSize(w, h);
33925         }
33926         if(Roo.isIE && this.tabs){
33927             this.tabs.el.repaint();
33928         }
33929     },
33930
33931     /**
33932      * Returns the container element for this region.
33933      * @return {Roo.Element}
33934      */
33935     getEl : function(){
33936         return this.el;
33937     },
33938
33939     /**
33940      * Hides this region.
33941      */
33942     hide : function(){
33943         //if(!this.collapsed){
33944             this.el.dom.style.left = "-2000px";
33945             this.el.hide();
33946         //}else{
33947          //   this.collapsedEl.dom.style.left = "-2000px";
33948          //   this.collapsedEl.hide();
33949        // }
33950         this.visible = false;
33951         this.fireEvent("visibilitychange", this, false);
33952     },
33953
33954     /**
33955      * Shows this region if it was previously hidden.
33956      */
33957     show : function(){
33958         //if(!this.collapsed){
33959             this.el.show();
33960         //}else{
33961         //    this.collapsedEl.show();
33962        // }
33963         this.visible = true;
33964         this.fireEvent("visibilitychange", this, true);
33965     },
33966 /*
33967     closeClicked : function(){
33968         if(this.activePanel){
33969             this.remove(this.activePanel);
33970         }
33971     },
33972
33973     collapseClick : function(e){
33974         if(this.isSlid){
33975            e.stopPropagation();
33976            this.slideIn();
33977         }else{
33978            e.stopPropagation();
33979            this.slideOut();
33980         }
33981     },
33982 */
33983     /**
33984      * Collapses this region.
33985      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33986      */
33987     /*
33988     collapse : function(skipAnim, skipCheck = false){
33989         if(this.collapsed) {
33990             return;
33991         }
33992         
33993         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33994             
33995             this.collapsed = true;
33996             if(this.split){
33997                 this.split.el.hide();
33998             }
33999             if(this.config.animate && skipAnim !== true){
34000                 this.fireEvent("invalidated", this);
34001                 this.animateCollapse();
34002             }else{
34003                 this.el.setLocation(-20000,-20000);
34004                 this.el.hide();
34005                 this.collapsedEl.show();
34006                 this.fireEvent("collapsed", this);
34007                 this.fireEvent("invalidated", this);
34008             }
34009         }
34010         
34011     },
34012 */
34013     animateCollapse : function(){
34014         // overridden
34015     },
34016
34017     /**
34018      * Expands this region if it was previously collapsed.
34019      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34020      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34021      */
34022     /*
34023     expand : function(e, skipAnim){
34024         if(e) {
34025             e.stopPropagation();
34026         }
34027         if(!this.collapsed || this.el.hasActiveFx()) {
34028             return;
34029         }
34030         if(this.isSlid){
34031             this.afterSlideIn();
34032             skipAnim = true;
34033         }
34034         this.collapsed = false;
34035         if(this.config.animate && skipAnim !== true){
34036             this.animateExpand();
34037         }else{
34038             this.el.show();
34039             if(this.split){
34040                 this.split.el.show();
34041             }
34042             this.collapsedEl.setLocation(-2000,-2000);
34043             this.collapsedEl.hide();
34044             this.fireEvent("invalidated", this);
34045             this.fireEvent("expanded", this);
34046         }
34047     },
34048 */
34049     animateExpand : function(){
34050         // overridden
34051     },
34052
34053     initTabs : function()
34054     {
34055         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34056         
34057         var ts = new Roo.bootstrap.panel.Tabs({
34058                 el: this.bodyEl.dom,
34059                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34060                 disableTooltips: this.config.disableTabTips,
34061                 toolbar : this.config.toolbar
34062             });
34063         
34064         if(this.config.hideTabs){
34065             ts.stripWrap.setDisplayed(false);
34066         }
34067         this.tabs = ts;
34068         ts.resizeTabs = this.config.resizeTabs === true;
34069         ts.minTabWidth = this.config.minTabWidth || 40;
34070         ts.maxTabWidth = this.config.maxTabWidth || 250;
34071         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34072         ts.monitorResize = false;
34073         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34074         ts.bodyEl.addClass('roo-layout-tabs-body');
34075         this.panels.each(this.initPanelAsTab, this);
34076     },
34077
34078     initPanelAsTab : function(panel){
34079         var ti = this.tabs.addTab(
34080             panel.getEl().id,
34081             panel.getTitle(),
34082             null,
34083             this.config.closeOnTab && panel.isClosable(),
34084             panel.tpl
34085         );
34086         if(panel.tabTip !== undefined){
34087             ti.setTooltip(panel.tabTip);
34088         }
34089         ti.on("activate", function(){
34090               this.setActivePanel(panel);
34091         }, this);
34092         
34093         if(this.config.closeOnTab){
34094             ti.on("beforeclose", function(t, e){
34095                 e.cancel = true;
34096                 this.remove(panel);
34097             }, this);
34098         }
34099         
34100         panel.tabItem = ti;
34101         
34102         return ti;
34103     },
34104
34105     updatePanelTitle : function(panel, title)
34106     {
34107         if(this.activePanel == panel){
34108             this.updateTitle(title);
34109         }
34110         if(this.tabs){
34111             var ti = this.tabs.getTab(panel.getEl().id);
34112             ti.setText(title);
34113             if(panel.tabTip !== undefined){
34114                 ti.setTooltip(panel.tabTip);
34115             }
34116         }
34117     },
34118
34119     updateTitle : function(title){
34120         if(this.titleTextEl && !this.config.title){
34121             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34122         }
34123     },
34124
34125     setActivePanel : function(panel)
34126     {
34127         panel = this.getPanel(panel);
34128         if(this.activePanel && this.activePanel != panel){
34129             this.activePanel.setActiveState(false);
34130         }
34131         this.activePanel = panel;
34132         panel.setActiveState(true);
34133         if(this.panelSize){
34134             panel.setSize(this.panelSize.width, this.panelSize.height);
34135         }
34136         if(this.closeBtn){
34137             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34138         }
34139         this.updateTitle(panel.getTitle());
34140         if(this.tabs){
34141             this.fireEvent("invalidated", this);
34142         }
34143         this.fireEvent("panelactivated", this, panel);
34144     },
34145
34146     /**
34147      * Shows the specified panel.
34148      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34149      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34150      */
34151     showPanel : function(panel)
34152     {
34153         panel = this.getPanel(panel);
34154         if(panel){
34155             if(this.tabs){
34156                 var tab = this.tabs.getTab(panel.getEl().id);
34157                 if(tab.isHidden()){
34158                     this.tabs.unhideTab(tab.id);
34159                 }
34160                 tab.activate();
34161             }else{
34162                 this.setActivePanel(panel);
34163             }
34164         }
34165         return panel;
34166     },
34167
34168     /**
34169      * Get the active panel for this region.
34170      * @return {Roo.ContentPanel} The active panel or null
34171      */
34172     getActivePanel : function(){
34173         return this.activePanel;
34174     },
34175
34176     validateVisibility : function(){
34177         if(this.panels.getCount() < 1){
34178             this.updateTitle("&#160;");
34179             this.closeBtn.hide();
34180             this.hide();
34181         }else{
34182             if(!this.isVisible()){
34183                 this.show();
34184             }
34185         }
34186     },
34187
34188     /**
34189      * Adds the passed ContentPanel(s) to this region.
34190      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34191      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34192      */
34193     add : function(panel)
34194     {
34195         if(arguments.length > 1){
34196             for(var i = 0, len = arguments.length; i < len; i++) {
34197                 this.add(arguments[i]);
34198             }
34199             return null;
34200         }
34201         
34202         // if we have not been rendered yet, then we can not really do much of this..
34203         if (!this.bodyEl) {
34204             this.unrendered_panels.push(panel);
34205             return panel;
34206         }
34207         
34208         
34209         
34210         
34211         if(this.hasPanel(panel)){
34212             this.showPanel(panel);
34213             return panel;
34214         }
34215         panel.setRegion(this);
34216         this.panels.add(panel);
34217        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34218             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34219             // and hide them... ???
34220             this.bodyEl.dom.appendChild(panel.getEl().dom);
34221             if(panel.background !== true){
34222                 this.setActivePanel(panel);
34223             }
34224             this.fireEvent("paneladded", this, panel);
34225             return panel;
34226         }
34227         */
34228         if(!this.tabs){
34229             this.initTabs();
34230         }else{
34231             this.initPanelAsTab(panel);
34232         }
34233         
34234         
34235         if(panel.background !== true){
34236             this.tabs.activate(panel.getEl().id);
34237         }
34238         this.fireEvent("paneladded", this, panel);
34239         return panel;
34240     },
34241
34242     /**
34243      * Hides the tab for the specified panel.
34244      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34245      */
34246     hidePanel : function(panel){
34247         if(this.tabs && (panel = this.getPanel(panel))){
34248             this.tabs.hideTab(panel.getEl().id);
34249         }
34250     },
34251
34252     /**
34253      * Unhides the tab for a previously hidden panel.
34254      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34255      */
34256     unhidePanel : function(panel){
34257         if(this.tabs && (panel = this.getPanel(panel))){
34258             this.tabs.unhideTab(panel.getEl().id);
34259         }
34260     },
34261
34262     clearPanels : function(){
34263         while(this.panels.getCount() > 0){
34264              this.remove(this.panels.first());
34265         }
34266     },
34267
34268     /**
34269      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34270      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34271      * @param {Boolean} preservePanel Overrides the config preservePanel option
34272      * @return {Roo.ContentPanel} The panel that was removed
34273      */
34274     remove : function(panel, preservePanel)
34275     {
34276         panel = this.getPanel(panel);
34277         if(!panel){
34278             return null;
34279         }
34280         var e = {};
34281         this.fireEvent("beforeremove", this, panel, e);
34282         if(e.cancel === true){
34283             return null;
34284         }
34285         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34286         var panelId = panel.getId();
34287         this.panels.removeKey(panelId);
34288         if(preservePanel){
34289             document.body.appendChild(panel.getEl().dom);
34290         }
34291         if(this.tabs){
34292             this.tabs.removeTab(panel.getEl().id);
34293         }else if (!preservePanel){
34294             this.bodyEl.dom.removeChild(panel.getEl().dom);
34295         }
34296         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34297             var p = this.panels.first();
34298             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34299             tempEl.appendChild(p.getEl().dom);
34300             this.bodyEl.update("");
34301             this.bodyEl.dom.appendChild(p.getEl().dom);
34302             tempEl = null;
34303             this.updateTitle(p.getTitle());
34304             this.tabs = null;
34305             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34306             this.setActivePanel(p);
34307         }
34308         panel.setRegion(null);
34309         if(this.activePanel == panel){
34310             this.activePanel = null;
34311         }
34312         if(this.config.autoDestroy !== false && preservePanel !== true){
34313             try{panel.destroy();}catch(e){}
34314         }
34315         this.fireEvent("panelremoved", this, panel);
34316         return panel;
34317     },
34318
34319     /**
34320      * Returns the TabPanel component used by this region
34321      * @return {Roo.TabPanel}
34322      */
34323     getTabs : function(){
34324         return this.tabs;
34325     },
34326
34327     createTool : function(parentEl, className){
34328         var btn = Roo.DomHelper.append(parentEl, {
34329             tag: "div",
34330             cls: "x-layout-tools-button",
34331             children: [ {
34332                 tag: "div",
34333                 cls: "roo-layout-tools-button-inner " + className,
34334                 html: "&#160;"
34335             }]
34336         }, true);
34337         btn.addClassOnOver("roo-layout-tools-button-over");
34338         return btn;
34339     }
34340 });/*
34341  * Based on:
34342  * Ext JS Library 1.1.1
34343  * Copyright(c) 2006-2007, Ext JS, LLC.
34344  *
34345  * Originally Released Under LGPL - original licence link has changed is not relivant.
34346  *
34347  * Fork - LGPL
34348  * <script type="text/javascript">
34349  */
34350  
34351
34352
34353 /**
34354  * @class Roo.SplitLayoutRegion
34355  * @extends Roo.LayoutRegion
34356  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34357  */
34358 Roo.bootstrap.layout.Split = function(config){
34359     this.cursor = config.cursor;
34360     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34361 };
34362
34363 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34364 {
34365     splitTip : "Drag to resize.",
34366     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34367     useSplitTips : false,
34368
34369     applyConfig : function(config){
34370         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34371     },
34372     
34373     onRender : function(ctr,pos) {
34374         
34375         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34376         if(!this.config.split){
34377             return;
34378         }
34379         if(!this.split){
34380             
34381             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34382                             tag: "div",
34383                             id: this.el.id + "-split",
34384                             cls: "roo-layout-split roo-layout-split-"+this.position,
34385                             html: "&#160;"
34386             });
34387             /** The SplitBar for this region 
34388             * @type Roo.SplitBar */
34389             // does not exist yet...
34390             Roo.log([this.position, this.orientation]);
34391             
34392             this.split = new Roo.bootstrap.SplitBar({
34393                 dragElement : splitEl,
34394                 resizingElement: this.el,
34395                 orientation : this.orientation
34396             });
34397             
34398             this.split.on("moved", this.onSplitMove, this);
34399             this.split.useShim = this.config.useShim === true;
34400             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34401             if(this.useSplitTips){
34402                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34403             }
34404             //if(config.collapsible){
34405             //    this.split.el.on("dblclick", this.collapse,  this);
34406             //}
34407         }
34408         if(typeof this.config.minSize != "undefined"){
34409             this.split.minSize = this.config.minSize;
34410         }
34411         if(typeof this.config.maxSize != "undefined"){
34412             this.split.maxSize = this.config.maxSize;
34413         }
34414         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34415             this.hideSplitter();
34416         }
34417         
34418     },
34419
34420     getHMaxSize : function(){
34421          var cmax = this.config.maxSize || 10000;
34422          var center = this.mgr.getRegion("center");
34423          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34424     },
34425
34426     getVMaxSize : function(){
34427          var cmax = this.config.maxSize || 10000;
34428          var center = this.mgr.getRegion("center");
34429          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34430     },
34431
34432     onSplitMove : function(split, newSize){
34433         this.fireEvent("resized", this, newSize);
34434     },
34435     
34436     /** 
34437      * Returns the {@link Roo.SplitBar} for this region.
34438      * @return {Roo.SplitBar}
34439      */
34440     getSplitBar : function(){
34441         return this.split;
34442     },
34443     
34444     hide : function(){
34445         this.hideSplitter();
34446         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34447     },
34448
34449     hideSplitter : function(){
34450         if(this.split){
34451             this.split.el.setLocation(-2000,-2000);
34452             this.split.el.hide();
34453         }
34454     },
34455
34456     show : function(){
34457         if(this.split){
34458             this.split.el.show();
34459         }
34460         Roo.bootstrap.layout.Split.superclass.show.call(this);
34461     },
34462     
34463     beforeSlide: function(){
34464         if(Roo.isGecko){// firefox overflow auto bug workaround
34465             this.bodyEl.clip();
34466             if(this.tabs) {
34467                 this.tabs.bodyEl.clip();
34468             }
34469             if(this.activePanel){
34470                 this.activePanel.getEl().clip();
34471                 
34472                 if(this.activePanel.beforeSlide){
34473                     this.activePanel.beforeSlide();
34474                 }
34475             }
34476         }
34477     },
34478     
34479     afterSlide : function(){
34480         if(Roo.isGecko){// firefox overflow auto bug workaround
34481             this.bodyEl.unclip();
34482             if(this.tabs) {
34483                 this.tabs.bodyEl.unclip();
34484             }
34485             if(this.activePanel){
34486                 this.activePanel.getEl().unclip();
34487                 if(this.activePanel.afterSlide){
34488                     this.activePanel.afterSlide();
34489                 }
34490             }
34491         }
34492     },
34493
34494     initAutoHide : function(){
34495         if(this.autoHide !== false){
34496             if(!this.autoHideHd){
34497                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34498                 this.autoHideHd = {
34499                     "mouseout": function(e){
34500                         if(!e.within(this.el, true)){
34501                             st.delay(500);
34502                         }
34503                     },
34504                     "mouseover" : function(e){
34505                         st.cancel();
34506                     },
34507                     scope : this
34508                 };
34509             }
34510             this.el.on(this.autoHideHd);
34511         }
34512     },
34513
34514     clearAutoHide : function(){
34515         if(this.autoHide !== false){
34516             this.el.un("mouseout", this.autoHideHd.mouseout);
34517             this.el.un("mouseover", this.autoHideHd.mouseover);
34518         }
34519     },
34520
34521     clearMonitor : function(){
34522         Roo.get(document).un("click", this.slideInIf, this);
34523     },
34524
34525     // these names are backwards but not changed for compat
34526     slideOut : function(){
34527         if(this.isSlid || this.el.hasActiveFx()){
34528             return;
34529         }
34530         this.isSlid = true;
34531         if(this.collapseBtn){
34532             this.collapseBtn.hide();
34533         }
34534         this.closeBtnState = this.closeBtn.getStyle('display');
34535         this.closeBtn.hide();
34536         if(this.stickBtn){
34537             this.stickBtn.show();
34538         }
34539         this.el.show();
34540         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34541         this.beforeSlide();
34542         this.el.setStyle("z-index", 10001);
34543         this.el.slideIn(this.getSlideAnchor(), {
34544             callback: function(){
34545                 this.afterSlide();
34546                 this.initAutoHide();
34547                 Roo.get(document).on("click", this.slideInIf, this);
34548                 this.fireEvent("slideshow", this);
34549             },
34550             scope: this,
34551             block: true
34552         });
34553     },
34554
34555     afterSlideIn : function(){
34556         this.clearAutoHide();
34557         this.isSlid = false;
34558         this.clearMonitor();
34559         this.el.setStyle("z-index", "");
34560         if(this.collapseBtn){
34561             this.collapseBtn.show();
34562         }
34563         this.closeBtn.setStyle('display', this.closeBtnState);
34564         if(this.stickBtn){
34565             this.stickBtn.hide();
34566         }
34567         this.fireEvent("slidehide", this);
34568     },
34569
34570     slideIn : function(cb){
34571         if(!this.isSlid || this.el.hasActiveFx()){
34572             Roo.callback(cb);
34573             return;
34574         }
34575         this.isSlid = false;
34576         this.beforeSlide();
34577         this.el.slideOut(this.getSlideAnchor(), {
34578             callback: function(){
34579                 this.el.setLeftTop(-10000, -10000);
34580                 this.afterSlide();
34581                 this.afterSlideIn();
34582                 Roo.callback(cb);
34583             },
34584             scope: this,
34585             block: true
34586         });
34587     },
34588     
34589     slideInIf : function(e){
34590         if(!e.within(this.el)){
34591             this.slideIn();
34592         }
34593     },
34594
34595     animateCollapse : function(){
34596         this.beforeSlide();
34597         this.el.setStyle("z-index", 20000);
34598         var anchor = this.getSlideAnchor();
34599         this.el.slideOut(anchor, {
34600             callback : function(){
34601                 this.el.setStyle("z-index", "");
34602                 this.collapsedEl.slideIn(anchor, {duration:.3});
34603                 this.afterSlide();
34604                 this.el.setLocation(-10000,-10000);
34605                 this.el.hide();
34606                 this.fireEvent("collapsed", this);
34607             },
34608             scope: this,
34609             block: true
34610         });
34611     },
34612
34613     animateExpand : function(){
34614         this.beforeSlide();
34615         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34616         this.el.setStyle("z-index", 20000);
34617         this.collapsedEl.hide({
34618             duration:.1
34619         });
34620         this.el.slideIn(this.getSlideAnchor(), {
34621             callback : function(){
34622                 this.el.setStyle("z-index", "");
34623                 this.afterSlide();
34624                 if(this.split){
34625                     this.split.el.show();
34626                 }
34627                 this.fireEvent("invalidated", this);
34628                 this.fireEvent("expanded", this);
34629             },
34630             scope: this,
34631             block: true
34632         });
34633     },
34634
34635     anchors : {
34636         "west" : "left",
34637         "east" : "right",
34638         "north" : "top",
34639         "south" : "bottom"
34640     },
34641
34642     sanchors : {
34643         "west" : "l",
34644         "east" : "r",
34645         "north" : "t",
34646         "south" : "b"
34647     },
34648
34649     canchors : {
34650         "west" : "tl-tr",
34651         "east" : "tr-tl",
34652         "north" : "tl-bl",
34653         "south" : "bl-tl"
34654     },
34655
34656     getAnchor : function(){
34657         return this.anchors[this.position];
34658     },
34659
34660     getCollapseAnchor : function(){
34661         return this.canchors[this.position];
34662     },
34663
34664     getSlideAnchor : function(){
34665         return this.sanchors[this.position];
34666     },
34667
34668     getAlignAdj : function(){
34669         var cm = this.cmargins;
34670         switch(this.position){
34671             case "west":
34672                 return [0, 0];
34673             break;
34674             case "east":
34675                 return [0, 0];
34676             break;
34677             case "north":
34678                 return [0, 0];
34679             break;
34680             case "south":
34681                 return [0, 0];
34682             break;
34683         }
34684     },
34685
34686     getExpandAdj : function(){
34687         var c = this.collapsedEl, cm = this.cmargins;
34688         switch(this.position){
34689             case "west":
34690                 return [-(cm.right+c.getWidth()+cm.left), 0];
34691             break;
34692             case "east":
34693                 return [cm.right+c.getWidth()+cm.left, 0];
34694             break;
34695             case "north":
34696                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34697             break;
34698             case "south":
34699                 return [0, cm.top+cm.bottom+c.getHeight()];
34700             break;
34701         }
34702     }
34703 });/*
34704  * Based on:
34705  * Ext JS Library 1.1.1
34706  * Copyright(c) 2006-2007, Ext JS, LLC.
34707  *
34708  * Originally Released Under LGPL - original licence link has changed is not relivant.
34709  *
34710  * Fork - LGPL
34711  * <script type="text/javascript">
34712  */
34713 /*
34714  * These classes are private internal classes
34715  */
34716 Roo.bootstrap.layout.Center = function(config){
34717     config.region = "center";
34718     Roo.bootstrap.layout.Region.call(this, config);
34719     this.visible = true;
34720     this.minWidth = config.minWidth || 20;
34721     this.minHeight = config.minHeight || 20;
34722 };
34723
34724 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34725     hide : function(){
34726         // center panel can't be hidden
34727     },
34728     
34729     show : function(){
34730         // center panel can't be hidden
34731     },
34732     
34733     getMinWidth: function(){
34734         return this.minWidth;
34735     },
34736     
34737     getMinHeight: function(){
34738         return this.minHeight;
34739     }
34740 });
34741
34742
34743
34744
34745  
34746
34747
34748
34749
34750
34751 Roo.bootstrap.layout.North = function(config)
34752 {
34753     config.region = 'north';
34754     config.cursor = 'n-resize';
34755     
34756     Roo.bootstrap.layout.Split.call(this, config);
34757     
34758     
34759     if(this.split){
34760         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34761         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34762         this.split.el.addClass("roo-layout-split-v");
34763     }
34764     var size = config.initialSize || config.height;
34765     if(typeof size != "undefined"){
34766         this.el.setHeight(size);
34767     }
34768 };
34769 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34770 {
34771     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34772     
34773     
34774     
34775     getBox : function(){
34776         if(this.collapsed){
34777             return this.collapsedEl.getBox();
34778         }
34779         var box = this.el.getBox();
34780         if(this.split){
34781             box.height += this.split.el.getHeight();
34782         }
34783         return box;
34784     },
34785     
34786     updateBox : function(box){
34787         if(this.split && !this.collapsed){
34788             box.height -= this.split.el.getHeight();
34789             this.split.el.setLeft(box.x);
34790             this.split.el.setTop(box.y+box.height);
34791             this.split.el.setWidth(box.width);
34792         }
34793         if(this.collapsed){
34794             this.updateBody(box.width, null);
34795         }
34796         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34797     }
34798 });
34799
34800
34801
34802
34803
34804 Roo.bootstrap.layout.South = function(config){
34805     config.region = 'south';
34806     config.cursor = 's-resize';
34807     Roo.bootstrap.layout.Split.call(this, config);
34808     if(this.split){
34809         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34810         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34811         this.split.el.addClass("roo-layout-split-v");
34812     }
34813     var size = config.initialSize || config.height;
34814     if(typeof size != "undefined"){
34815         this.el.setHeight(size);
34816     }
34817 };
34818
34819 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34820     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34821     getBox : function(){
34822         if(this.collapsed){
34823             return this.collapsedEl.getBox();
34824         }
34825         var box = this.el.getBox();
34826         if(this.split){
34827             var sh = this.split.el.getHeight();
34828             box.height += sh;
34829             box.y -= sh;
34830         }
34831         return box;
34832     },
34833     
34834     updateBox : function(box){
34835         if(this.split && !this.collapsed){
34836             var sh = this.split.el.getHeight();
34837             box.height -= sh;
34838             box.y += sh;
34839             this.split.el.setLeft(box.x);
34840             this.split.el.setTop(box.y-sh);
34841             this.split.el.setWidth(box.width);
34842         }
34843         if(this.collapsed){
34844             this.updateBody(box.width, null);
34845         }
34846         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34847     }
34848 });
34849
34850 Roo.bootstrap.layout.East = function(config){
34851     config.region = "east";
34852     config.cursor = "e-resize";
34853     Roo.bootstrap.layout.Split.call(this, config);
34854     if(this.split){
34855         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34856         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34857         this.split.el.addClass("roo-layout-split-h");
34858     }
34859     var size = config.initialSize || config.width;
34860     if(typeof size != "undefined"){
34861         this.el.setWidth(size);
34862     }
34863 };
34864 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34865     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34866     getBox : function(){
34867         if(this.collapsed){
34868             return this.collapsedEl.getBox();
34869         }
34870         var box = this.el.getBox();
34871         if(this.split){
34872             var sw = this.split.el.getWidth();
34873             box.width += sw;
34874             box.x -= sw;
34875         }
34876         return box;
34877     },
34878
34879     updateBox : function(box){
34880         if(this.split && !this.collapsed){
34881             var sw = this.split.el.getWidth();
34882             box.width -= sw;
34883             this.split.el.setLeft(box.x);
34884             this.split.el.setTop(box.y);
34885             this.split.el.setHeight(box.height);
34886             box.x += sw;
34887         }
34888         if(this.collapsed){
34889             this.updateBody(null, box.height);
34890         }
34891         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34892     }
34893 });
34894
34895 Roo.bootstrap.layout.West = function(config){
34896     config.region = "west";
34897     config.cursor = "w-resize";
34898     
34899     Roo.bootstrap.layout.Split.call(this, config);
34900     if(this.split){
34901         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34902         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34903         this.split.el.addClass("roo-layout-split-h");
34904     }
34905     
34906 };
34907 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34908     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34909     
34910     onRender: function(ctr, pos)
34911     {
34912         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34913         var size = this.config.initialSize || this.config.width;
34914         if(typeof size != "undefined"){
34915             this.el.setWidth(size);
34916         }
34917     },
34918     
34919     getBox : function(){
34920         if(this.collapsed){
34921             return this.collapsedEl.getBox();
34922         }
34923         var box = this.el.getBox();
34924         if(this.split){
34925             box.width += this.split.el.getWidth();
34926         }
34927         return box;
34928     },
34929     
34930     updateBox : function(box){
34931         if(this.split && !this.collapsed){
34932             var sw = this.split.el.getWidth();
34933             box.width -= sw;
34934             this.split.el.setLeft(box.x+box.width);
34935             this.split.el.setTop(box.y);
34936             this.split.el.setHeight(box.height);
34937         }
34938         if(this.collapsed){
34939             this.updateBody(null, box.height);
34940         }
34941         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34942     }
34943 });
34944 Roo.namespace("Roo.bootstrap.panel");/*
34945  * Based on:
34946  * Ext JS Library 1.1.1
34947  * Copyright(c) 2006-2007, Ext JS, LLC.
34948  *
34949  * Originally Released Under LGPL - original licence link has changed is not relivant.
34950  *
34951  * Fork - LGPL
34952  * <script type="text/javascript">
34953  */
34954 /**
34955  * @class Roo.ContentPanel
34956  * @extends Roo.util.Observable
34957  * A basic ContentPanel element.
34958  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34959  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34960  * @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
34961  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34962  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34963  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34964  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34965  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34966  * @cfg {String} title          The title for this panel
34967  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34968  * @cfg {String} url            Calls {@link #setUrl} with this value
34969  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34970  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34971  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34972  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34973  * @cfg {Boolean} badges render the badges
34974
34975  * @constructor
34976  * Create a new ContentPanel.
34977  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34978  * @param {String/Object} config A string to set only the title or a config object
34979  * @param {String} content (optional) Set the HTML content for this panel
34980  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34981  */
34982 Roo.bootstrap.panel.Content = function( config){
34983     
34984     this.tpl = config.tpl || false;
34985     
34986     var el = config.el;
34987     var content = config.content;
34988
34989     if(config.autoCreate){ // xtype is available if this is called from factory
34990         el = Roo.id();
34991     }
34992     this.el = Roo.get(el);
34993     if(!this.el && config && config.autoCreate){
34994         if(typeof config.autoCreate == "object"){
34995             if(!config.autoCreate.id){
34996                 config.autoCreate.id = config.id||el;
34997             }
34998             this.el = Roo.DomHelper.append(document.body,
34999                         config.autoCreate, true);
35000         }else{
35001             var elcfg =  {   tag: "div",
35002                             cls: "roo-layout-inactive-content",
35003                             id: config.id||el
35004                             };
35005             if (config.html) {
35006                 elcfg.html = config.html;
35007                 
35008             }
35009                         
35010             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35011         }
35012     } 
35013     this.closable = false;
35014     this.loaded = false;
35015     this.active = false;
35016    
35017       
35018     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35019         
35020         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35021         
35022         this.wrapEl = this.el; //this.el.wrap();
35023         var ti = [];
35024         if (config.toolbar.items) {
35025             ti = config.toolbar.items ;
35026             delete config.toolbar.items ;
35027         }
35028         
35029         var nitems = [];
35030         this.toolbar.render(this.wrapEl, 'before');
35031         for(var i =0;i < ti.length;i++) {
35032           //  Roo.log(['add child', items[i]]);
35033             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35034         }
35035         this.toolbar.items = nitems;
35036         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35037         delete config.toolbar;
35038         
35039     }
35040     /*
35041     // xtype created footer. - not sure if will work as we normally have to render first..
35042     if (this.footer && !this.footer.el && this.footer.xtype) {
35043         if (!this.wrapEl) {
35044             this.wrapEl = this.el.wrap();
35045         }
35046     
35047         this.footer.container = this.wrapEl.createChild();
35048          
35049         this.footer = Roo.factory(this.footer, Roo);
35050         
35051     }
35052     */
35053     
35054      if(typeof config == "string"){
35055         this.title = config;
35056     }else{
35057         Roo.apply(this, config);
35058     }
35059     
35060     if(this.resizeEl){
35061         this.resizeEl = Roo.get(this.resizeEl, true);
35062     }else{
35063         this.resizeEl = this.el;
35064     }
35065     // handle view.xtype
35066     
35067  
35068     
35069     
35070     this.addEvents({
35071         /**
35072          * @event activate
35073          * Fires when this panel is activated. 
35074          * @param {Roo.ContentPanel} this
35075          */
35076         "activate" : true,
35077         /**
35078          * @event deactivate
35079          * Fires when this panel is activated. 
35080          * @param {Roo.ContentPanel} this
35081          */
35082         "deactivate" : true,
35083
35084         /**
35085          * @event resize
35086          * Fires when this panel is resized if fitToFrame is true.
35087          * @param {Roo.ContentPanel} this
35088          * @param {Number} width The width after any component adjustments
35089          * @param {Number} height The height after any component adjustments
35090          */
35091         "resize" : true,
35092         
35093          /**
35094          * @event render
35095          * Fires when this tab is created
35096          * @param {Roo.ContentPanel} this
35097          */
35098         "render" : true
35099         
35100         
35101         
35102     });
35103     
35104
35105     
35106     
35107     if(this.autoScroll){
35108         this.resizeEl.setStyle("overflow", "auto");
35109     } else {
35110         // fix randome scrolling
35111         //this.el.on('scroll', function() {
35112         //    Roo.log('fix random scolling');
35113         //    this.scrollTo('top',0); 
35114         //});
35115     }
35116     content = content || this.content;
35117     if(content){
35118         this.setContent(content);
35119     }
35120     if(config && config.url){
35121         this.setUrl(this.url, this.params, this.loadOnce);
35122     }
35123     
35124     
35125     
35126     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35127     
35128     if (this.view && typeof(this.view.xtype) != 'undefined') {
35129         this.view.el = this.el.appendChild(document.createElement("div"));
35130         this.view = Roo.factory(this.view); 
35131         this.view.render  &&  this.view.render(false, '');  
35132     }
35133     
35134     
35135     this.fireEvent('render', this);
35136 };
35137
35138 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35139     
35140     tabTip : '',
35141     
35142     setRegion : function(region){
35143         this.region = region;
35144         this.setActiveClass(region && !this.background);
35145     },
35146     
35147     
35148     setActiveClass: function(state)
35149     {
35150         if(state){
35151            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35152            this.el.setStyle('position','relative');
35153         }else{
35154            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35155            this.el.setStyle('position', 'absolute');
35156         } 
35157     },
35158     
35159     /**
35160      * Returns the toolbar for this Panel if one was configured. 
35161      * @return {Roo.Toolbar} 
35162      */
35163     getToolbar : function(){
35164         return this.toolbar;
35165     },
35166     
35167     setActiveState : function(active)
35168     {
35169         this.active = active;
35170         this.setActiveClass(active);
35171         if(!active){
35172             this.fireEvent("deactivate", this);
35173         }else{
35174             this.fireEvent("activate", this);
35175         }
35176     },
35177     /**
35178      * Updates this panel's element
35179      * @param {String} content The new content
35180      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35181     */
35182     setContent : function(content, loadScripts){
35183         this.el.update(content, loadScripts);
35184     },
35185
35186     ignoreResize : function(w, h){
35187         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35188             return true;
35189         }else{
35190             this.lastSize = {width: w, height: h};
35191             return false;
35192         }
35193     },
35194     /**
35195      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35196      * @return {Roo.UpdateManager} The UpdateManager
35197      */
35198     getUpdateManager : function(){
35199         return this.el.getUpdateManager();
35200     },
35201      /**
35202      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35203      * @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:
35204 <pre><code>
35205 panel.load({
35206     url: "your-url.php",
35207     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35208     callback: yourFunction,
35209     scope: yourObject, //(optional scope)
35210     discardUrl: false,
35211     nocache: false,
35212     text: "Loading...",
35213     timeout: 30,
35214     scripts: false
35215 });
35216 </code></pre>
35217      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35218      * 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.
35219      * @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}
35220      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35221      * @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.
35222      * @return {Roo.ContentPanel} this
35223      */
35224     load : function(){
35225         var um = this.el.getUpdateManager();
35226         um.update.apply(um, arguments);
35227         return this;
35228     },
35229
35230
35231     /**
35232      * 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.
35233      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35234      * @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)
35235      * @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)
35236      * @return {Roo.UpdateManager} The UpdateManager
35237      */
35238     setUrl : function(url, params, loadOnce){
35239         if(this.refreshDelegate){
35240             this.removeListener("activate", this.refreshDelegate);
35241         }
35242         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35243         this.on("activate", this.refreshDelegate);
35244         return this.el.getUpdateManager();
35245     },
35246     
35247     _handleRefresh : function(url, params, loadOnce){
35248         if(!loadOnce || !this.loaded){
35249             var updater = this.el.getUpdateManager();
35250             updater.update(url, params, this._setLoaded.createDelegate(this));
35251         }
35252     },
35253     
35254     _setLoaded : function(){
35255         this.loaded = true;
35256     }, 
35257     
35258     /**
35259      * Returns this panel's id
35260      * @return {String} 
35261      */
35262     getId : function(){
35263         return this.el.id;
35264     },
35265     
35266     /** 
35267      * Returns this panel's element - used by regiosn to add.
35268      * @return {Roo.Element} 
35269      */
35270     getEl : function(){
35271         return this.wrapEl || this.el;
35272     },
35273     
35274    
35275     
35276     adjustForComponents : function(width, height)
35277     {
35278         //Roo.log('adjustForComponents ');
35279         if(this.resizeEl != this.el){
35280             width -= this.el.getFrameWidth('lr');
35281             height -= this.el.getFrameWidth('tb');
35282         }
35283         if(this.toolbar){
35284             var te = this.toolbar.getEl();
35285             height -= te.getHeight();
35286             te.setWidth(width);
35287         }
35288         if(this.footer){
35289             var te = this.footer.getEl();
35290             Roo.log("footer:" + te.getHeight());
35291             
35292             height -= te.getHeight();
35293             te.setWidth(width);
35294         }
35295         
35296         
35297         if(this.adjustments){
35298             width += this.adjustments[0];
35299             height += this.adjustments[1];
35300         }
35301         return {"width": width, "height": height};
35302     },
35303     
35304     setSize : function(width, height){
35305         if(this.fitToFrame && !this.ignoreResize(width, height)){
35306             if(this.fitContainer && this.resizeEl != this.el){
35307                 this.el.setSize(width, height);
35308             }
35309             var size = this.adjustForComponents(width, height);
35310             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35311             this.fireEvent('resize', this, size.width, size.height);
35312         }
35313     },
35314     
35315     /**
35316      * Returns this panel's title
35317      * @return {String} 
35318      */
35319     getTitle : function(){
35320         return this.title;
35321     },
35322     
35323     /**
35324      * Set this panel's title
35325      * @param {String} title
35326      */
35327     setTitle : function(title){
35328         this.title = title;
35329         if(this.region){
35330             this.region.updatePanelTitle(this, title);
35331         }
35332     },
35333     
35334     /**
35335      * Returns true is this panel was configured to be closable
35336      * @return {Boolean} 
35337      */
35338     isClosable : function(){
35339         return this.closable;
35340     },
35341     
35342     beforeSlide : function(){
35343         this.el.clip();
35344         this.resizeEl.clip();
35345     },
35346     
35347     afterSlide : function(){
35348         this.el.unclip();
35349         this.resizeEl.unclip();
35350     },
35351     
35352     /**
35353      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35354      *   Will fail silently if the {@link #setUrl} method has not been called.
35355      *   This does not activate the panel, just updates its content.
35356      */
35357     refresh : function(){
35358         if(this.refreshDelegate){
35359            this.loaded = false;
35360            this.refreshDelegate();
35361         }
35362     },
35363     
35364     /**
35365      * Destroys this panel
35366      */
35367     destroy : function(){
35368         this.el.removeAllListeners();
35369         var tempEl = document.createElement("span");
35370         tempEl.appendChild(this.el.dom);
35371         tempEl.innerHTML = "";
35372         this.el.remove();
35373         this.el = null;
35374     },
35375     
35376     /**
35377      * form - if the content panel contains a form - this is a reference to it.
35378      * @type {Roo.form.Form}
35379      */
35380     form : false,
35381     /**
35382      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35383      *    This contains a reference to it.
35384      * @type {Roo.View}
35385      */
35386     view : false,
35387     
35388       /**
35389      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35390      * <pre><code>
35391
35392 layout.addxtype({
35393        xtype : 'Form',
35394        items: [ .... ]
35395    }
35396 );
35397
35398 </code></pre>
35399      * @param {Object} cfg Xtype definition of item to add.
35400      */
35401     
35402     
35403     getChildContainer: function () {
35404         return this.getEl();
35405     }
35406     
35407     
35408     /*
35409         var  ret = new Roo.factory(cfg);
35410         return ret;
35411         
35412         
35413         // add form..
35414         if (cfg.xtype.match(/^Form$/)) {
35415             
35416             var el;
35417             //if (this.footer) {
35418             //    el = this.footer.container.insertSibling(false, 'before');
35419             //} else {
35420                 el = this.el.createChild();
35421             //}
35422
35423             this.form = new  Roo.form.Form(cfg);
35424             
35425             
35426             if ( this.form.allItems.length) {
35427                 this.form.render(el.dom);
35428             }
35429             return this.form;
35430         }
35431         // should only have one of theses..
35432         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35433             // views.. should not be just added - used named prop 'view''
35434             
35435             cfg.el = this.el.appendChild(document.createElement("div"));
35436             // factory?
35437             
35438             var ret = new Roo.factory(cfg);
35439              
35440              ret.render && ret.render(false, ''); // render blank..
35441             this.view = ret;
35442             return ret;
35443         }
35444         return false;
35445     }
35446     \*/
35447 });
35448  
35449 /**
35450  * @class Roo.bootstrap.panel.Grid
35451  * @extends Roo.bootstrap.panel.Content
35452  * @constructor
35453  * Create a new GridPanel.
35454  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35455  * @param {Object} config A the config object
35456   
35457  */
35458
35459
35460
35461 Roo.bootstrap.panel.Grid = function(config)
35462 {
35463     
35464       
35465     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35466         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35467
35468     config.el = this.wrapper;
35469     //this.el = this.wrapper;
35470     
35471       if (config.container) {
35472         // ctor'ed from a Border/panel.grid
35473         
35474         
35475         this.wrapper.setStyle("overflow", "hidden");
35476         this.wrapper.addClass('roo-grid-container');
35477
35478     }
35479     
35480     
35481     if(config.toolbar){
35482         var tool_el = this.wrapper.createChild();    
35483         this.toolbar = Roo.factory(config.toolbar);
35484         var ti = [];
35485         if (config.toolbar.items) {
35486             ti = config.toolbar.items ;
35487             delete config.toolbar.items ;
35488         }
35489         
35490         var nitems = [];
35491         this.toolbar.render(tool_el);
35492         for(var i =0;i < ti.length;i++) {
35493           //  Roo.log(['add child', items[i]]);
35494             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35495         }
35496         this.toolbar.items = nitems;
35497         
35498         delete config.toolbar;
35499     }
35500     
35501     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35502     config.grid.scrollBody = true;;
35503     config.grid.monitorWindowResize = false; // turn off autosizing
35504     config.grid.autoHeight = false;
35505     config.grid.autoWidth = false;
35506     
35507     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35508     
35509     if (config.background) {
35510         // render grid on panel activation (if panel background)
35511         this.on('activate', function(gp) {
35512             if (!gp.grid.rendered) {
35513                 gp.grid.render(this.wrapper);
35514                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35515             }
35516         });
35517             
35518     } else {
35519         this.grid.render(this.wrapper);
35520         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35521
35522     }
35523     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35524     // ??? needed ??? config.el = this.wrapper;
35525     
35526     
35527     
35528   
35529     // xtype created footer. - not sure if will work as we normally have to render first..
35530     if (this.footer && !this.footer.el && this.footer.xtype) {
35531         
35532         var ctr = this.grid.getView().getFooterPanel(true);
35533         this.footer.dataSource = this.grid.dataSource;
35534         this.footer = Roo.factory(this.footer, Roo);
35535         this.footer.render(ctr);
35536         
35537     }
35538     
35539     
35540     
35541     
35542      
35543 };
35544
35545 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35546     getId : function(){
35547         return this.grid.id;
35548     },
35549     
35550     /**
35551      * Returns the grid for this panel
35552      * @return {Roo.bootstrap.Table} 
35553      */
35554     getGrid : function(){
35555         return this.grid;    
35556     },
35557     
35558     setSize : function(width, height){
35559         if(!this.ignoreResize(width, height)){
35560             var grid = this.grid;
35561             var size = this.adjustForComponents(width, height);
35562             var gridel = grid.getGridEl();
35563             gridel.setSize(size.width, size.height);
35564             /*
35565             var thd = grid.getGridEl().select('thead',true).first();
35566             var tbd = grid.getGridEl().select('tbody', true).first();
35567             if (tbd) {
35568                 tbd.setSize(width, height - thd.getHeight());
35569             }
35570             */
35571             grid.autoSize();
35572         }
35573     },
35574      
35575     
35576     
35577     beforeSlide : function(){
35578         this.grid.getView().scroller.clip();
35579     },
35580     
35581     afterSlide : function(){
35582         this.grid.getView().scroller.unclip();
35583     },
35584     
35585     destroy : function(){
35586         this.grid.destroy();
35587         delete this.grid;
35588         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35589     }
35590 });
35591
35592 /**
35593  * @class Roo.bootstrap.panel.Nest
35594  * @extends Roo.bootstrap.panel.Content
35595  * @constructor
35596  * Create a new Panel, that can contain a layout.Border.
35597  * 
35598  * 
35599  * @param {Roo.BorderLayout} layout The layout for this panel
35600  * @param {String/Object} config A string to set only the title or a config object
35601  */
35602 Roo.bootstrap.panel.Nest = function(config)
35603 {
35604     // construct with only one argument..
35605     /* FIXME - implement nicer consturctors
35606     if (layout.layout) {
35607         config = layout;
35608         layout = config.layout;
35609         delete config.layout;
35610     }
35611     if (layout.xtype && !layout.getEl) {
35612         // then layout needs constructing..
35613         layout = Roo.factory(layout, Roo);
35614     }
35615     */
35616     
35617     config.el =  config.layout.getEl();
35618     
35619     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35620     
35621     config.layout.monitorWindowResize = false; // turn off autosizing
35622     this.layout = config.layout;
35623     this.layout.getEl().addClass("roo-layout-nested-layout");
35624     
35625     
35626     
35627     
35628 };
35629
35630 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35631
35632     setSize : function(width, height){
35633         if(!this.ignoreResize(width, height)){
35634             var size = this.adjustForComponents(width, height);
35635             var el = this.layout.getEl();
35636             if (size.height < 1) {
35637                 el.setWidth(size.width);   
35638             } else {
35639                 el.setSize(size.width, size.height);
35640             }
35641             var touch = el.dom.offsetWidth;
35642             this.layout.layout();
35643             // ie requires a double layout on the first pass
35644             if(Roo.isIE && !this.initialized){
35645                 this.initialized = true;
35646                 this.layout.layout();
35647             }
35648         }
35649     },
35650     
35651     // activate all subpanels if not currently active..
35652     
35653     setActiveState : function(active){
35654         this.active = active;
35655         this.setActiveClass(active);
35656         
35657         if(!active){
35658             this.fireEvent("deactivate", this);
35659             return;
35660         }
35661         
35662         this.fireEvent("activate", this);
35663         // not sure if this should happen before or after..
35664         if (!this.layout) {
35665             return; // should not happen..
35666         }
35667         var reg = false;
35668         for (var r in this.layout.regions) {
35669             reg = this.layout.getRegion(r);
35670             if (reg.getActivePanel()) {
35671                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35672                 reg.setActivePanel(reg.getActivePanel());
35673                 continue;
35674             }
35675             if (!reg.panels.length) {
35676                 continue;
35677             }
35678             reg.showPanel(reg.getPanel(0));
35679         }
35680         
35681         
35682         
35683         
35684     },
35685     
35686     /**
35687      * Returns the nested BorderLayout for this panel
35688      * @return {Roo.BorderLayout} 
35689      */
35690     getLayout : function(){
35691         return this.layout;
35692     },
35693     
35694      /**
35695      * Adds a xtype elements to the layout of the nested panel
35696      * <pre><code>
35697
35698 panel.addxtype({
35699        xtype : 'ContentPanel',
35700        region: 'west',
35701        items: [ .... ]
35702    }
35703 );
35704
35705 panel.addxtype({
35706         xtype : 'NestedLayoutPanel',
35707         region: 'west',
35708         layout: {
35709            center: { },
35710            west: { }   
35711         },
35712         items : [ ... list of content panels or nested layout panels.. ]
35713    }
35714 );
35715 </code></pre>
35716      * @param {Object} cfg Xtype definition of item to add.
35717      */
35718     addxtype : function(cfg) {
35719         return this.layout.addxtype(cfg);
35720     
35721     }
35722 });        /*
35723  * Based on:
35724  * Ext JS Library 1.1.1
35725  * Copyright(c) 2006-2007, Ext JS, LLC.
35726  *
35727  * Originally Released Under LGPL - original licence link has changed is not relivant.
35728  *
35729  * Fork - LGPL
35730  * <script type="text/javascript">
35731  */
35732 /**
35733  * @class Roo.TabPanel
35734  * @extends Roo.util.Observable
35735  * A lightweight tab container.
35736  * <br><br>
35737  * Usage:
35738  * <pre><code>
35739 // basic tabs 1, built from existing content
35740 var tabs = new Roo.TabPanel("tabs1");
35741 tabs.addTab("script", "View Script");
35742 tabs.addTab("markup", "View Markup");
35743 tabs.activate("script");
35744
35745 // more advanced tabs, built from javascript
35746 var jtabs = new Roo.TabPanel("jtabs");
35747 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35748
35749 // set up the UpdateManager
35750 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35751 var updater = tab2.getUpdateManager();
35752 updater.setDefaultUrl("ajax1.htm");
35753 tab2.on('activate', updater.refresh, updater, true);
35754
35755 // Use setUrl for Ajax loading
35756 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35757 tab3.setUrl("ajax2.htm", null, true);
35758
35759 // Disabled tab
35760 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35761 tab4.disable();
35762
35763 jtabs.activate("jtabs-1");
35764  * </code></pre>
35765  * @constructor
35766  * Create a new TabPanel.
35767  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35768  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35769  */
35770 Roo.bootstrap.panel.Tabs = function(config){
35771     /**
35772     * The container element for this TabPanel.
35773     * @type Roo.Element
35774     */
35775     this.el = Roo.get(config.el);
35776     delete config.el;
35777     if(config){
35778         if(typeof config == "boolean"){
35779             this.tabPosition = config ? "bottom" : "top";
35780         }else{
35781             Roo.apply(this, config);
35782         }
35783     }
35784     
35785     if(this.tabPosition == "bottom"){
35786         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35787         this.el.addClass("roo-tabs-bottom");
35788     }
35789     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35790     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35791     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35792     if(Roo.isIE){
35793         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35794     }
35795     if(this.tabPosition != "bottom"){
35796         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35797          * @type Roo.Element
35798          */
35799         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35800         this.el.addClass("roo-tabs-top");
35801     }
35802     this.items = [];
35803
35804     this.bodyEl.setStyle("position", "relative");
35805
35806     this.active = null;
35807     this.activateDelegate = this.activate.createDelegate(this);
35808
35809     this.addEvents({
35810         /**
35811          * @event tabchange
35812          * Fires when the active tab changes
35813          * @param {Roo.TabPanel} this
35814          * @param {Roo.TabPanelItem} activePanel The new active tab
35815          */
35816         "tabchange": true,
35817         /**
35818          * @event beforetabchange
35819          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35820          * @param {Roo.TabPanel} this
35821          * @param {Object} e Set cancel to true on this object to cancel the tab change
35822          * @param {Roo.TabPanelItem} tab The tab being changed to
35823          */
35824         "beforetabchange" : true
35825     });
35826
35827     Roo.EventManager.onWindowResize(this.onResize, this);
35828     this.cpad = this.el.getPadding("lr");
35829     this.hiddenCount = 0;
35830
35831
35832     // toolbar on the tabbar support...
35833     if (this.toolbar) {
35834         alert("no toolbar support yet");
35835         this.toolbar  = false;
35836         /*
35837         var tcfg = this.toolbar;
35838         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35839         this.toolbar = new Roo.Toolbar(tcfg);
35840         if (Roo.isSafari) {
35841             var tbl = tcfg.container.child('table', true);
35842             tbl.setAttribute('width', '100%');
35843         }
35844         */
35845         
35846     }
35847    
35848
35849
35850     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35851 };
35852
35853 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35854     /*
35855      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35856      */
35857     tabPosition : "top",
35858     /*
35859      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35860      */
35861     currentTabWidth : 0,
35862     /*
35863      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35864      */
35865     minTabWidth : 40,
35866     /*
35867      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35868      */
35869     maxTabWidth : 250,
35870     /*
35871      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35872      */
35873     preferredTabWidth : 175,
35874     /*
35875      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35876      */
35877     resizeTabs : false,
35878     /*
35879      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35880      */
35881     monitorResize : true,
35882     /*
35883      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35884      */
35885     toolbar : false,
35886
35887     /**
35888      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35889      * @param {String} id The id of the div to use <b>or create</b>
35890      * @param {String} text The text for the tab
35891      * @param {String} content (optional) Content to put in the TabPanelItem body
35892      * @param {Boolean} closable (optional) True to create a close icon on the tab
35893      * @return {Roo.TabPanelItem} The created TabPanelItem
35894      */
35895     addTab : function(id, text, content, closable, tpl)
35896     {
35897         var item = new Roo.bootstrap.panel.TabItem({
35898             panel: this,
35899             id : id,
35900             text : text,
35901             closable : closable,
35902             tpl : tpl
35903         });
35904         this.addTabItem(item);
35905         if(content){
35906             item.setContent(content);
35907         }
35908         return item;
35909     },
35910
35911     /**
35912      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35913      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35914      * @return {Roo.TabPanelItem}
35915      */
35916     getTab : function(id){
35917         return this.items[id];
35918     },
35919
35920     /**
35921      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35922      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35923      */
35924     hideTab : function(id){
35925         var t = this.items[id];
35926         if(!t.isHidden()){
35927            t.setHidden(true);
35928            this.hiddenCount++;
35929            this.autoSizeTabs();
35930         }
35931     },
35932
35933     /**
35934      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35935      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35936      */
35937     unhideTab : function(id){
35938         var t = this.items[id];
35939         if(t.isHidden()){
35940            t.setHidden(false);
35941            this.hiddenCount--;
35942            this.autoSizeTabs();
35943         }
35944     },
35945
35946     /**
35947      * Adds an existing {@link Roo.TabPanelItem}.
35948      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35949      */
35950     addTabItem : function(item){
35951         this.items[item.id] = item;
35952         this.items.push(item);
35953       //  if(this.resizeTabs){
35954     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35955   //         this.autoSizeTabs();
35956 //        }else{
35957 //            item.autoSize();
35958        // }
35959     },
35960
35961     /**
35962      * Removes a {@link Roo.TabPanelItem}.
35963      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35964      */
35965     removeTab : function(id){
35966         var items = this.items;
35967         var tab = items[id];
35968         if(!tab) { return; }
35969         var index = items.indexOf(tab);
35970         if(this.active == tab && items.length > 1){
35971             var newTab = this.getNextAvailable(index);
35972             if(newTab) {
35973                 newTab.activate();
35974             }
35975         }
35976         this.stripEl.dom.removeChild(tab.pnode.dom);
35977         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35978             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35979         }
35980         items.splice(index, 1);
35981         delete this.items[tab.id];
35982         tab.fireEvent("close", tab);
35983         tab.purgeListeners();
35984         this.autoSizeTabs();
35985     },
35986
35987     getNextAvailable : function(start){
35988         var items = this.items;
35989         var index = start;
35990         // look for a next tab that will slide over to
35991         // replace the one being removed
35992         while(index < items.length){
35993             var item = items[++index];
35994             if(item && !item.isHidden()){
35995                 return item;
35996             }
35997         }
35998         // if one isn't found select the previous tab (on the left)
35999         index = start;
36000         while(index >= 0){
36001             var item = items[--index];
36002             if(item && !item.isHidden()){
36003                 return item;
36004             }
36005         }
36006         return null;
36007     },
36008
36009     /**
36010      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36011      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36012      */
36013     disableTab : function(id){
36014         var tab = this.items[id];
36015         if(tab && this.active != tab){
36016             tab.disable();
36017         }
36018     },
36019
36020     /**
36021      * Enables a {@link Roo.TabPanelItem} that is disabled.
36022      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36023      */
36024     enableTab : function(id){
36025         var tab = this.items[id];
36026         tab.enable();
36027     },
36028
36029     /**
36030      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36031      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36032      * @return {Roo.TabPanelItem} The TabPanelItem.
36033      */
36034     activate : function(id){
36035         var tab = this.items[id];
36036         if(!tab){
36037             return null;
36038         }
36039         if(tab == this.active || tab.disabled){
36040             return tab;
36041         }
36042         var e = {};
36043         this.fireEvent("beforetabchange", this, e, tab);
36044         if(e.cancel !== true && !tab.disabled){
36045             if(this.active){
36046                 this.active.hide();
36047             }
36048             this.active = this.items[id];
36049             this.active.show();
36050             this.fireEvent("tabchange", this, this.active);
36051         }
36052         return tab;
36053     },
36054
36055     /**
36056      * Gets the active {@link Roo.TabPanelItem}.
36057      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36058      */
36059     getActiveTab : function(){
36060         return this.active;
36061     },
36062
36063     /**
36064      * Updates the tab body element to fit the height of the container element
36065      * for overflow scrolling
36066      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36067      */
36068     syncHeight : function(targetHeight){
36069         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36070         var bm = this.bodyEl.getMargins();
36071         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36072         this.bodyEl.setHeight(newHeight);
36073         return newHeight;
36074     },
36075
36076     onResize : function(){
36077         if(this.monitorResize){
36078             this.autoSizeTabs();
36079         }
36080     },
36081
36082     /**
36083      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36084      */
36085     beginUpdate : function(){
36086         this.updating = true;
36087     },
36088
36089     /**
36090      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36091      */
36092     endUpdate : function(){
36093         this.updating = false;
36094         this.autoSizeTabs();
36095     },
36096
36097     /**
36098      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36099      */
36100     autoSizeTabs : function(){
36101         var count = this.items.length;
36102         var vcount = count - this.hiddenCount;
36103         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36104             return;
36105         }
36106         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36107         var availWidth = Math.floor(w / vcount);
36108         var b = this.stripBody;
36109         if(b.getWidth() > w){
36110             var tabs = this.items;
36111             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36112             if(availWidth < this.minTabWidth){
36113                 /*if(!this.sleft){    // incomplete scrolling code
36114                     this.createScrollButtons();
36115                 }
36116                 this.showScroll();
36117                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36118             }
36119         }else{
36120             if(this.currentTabWidth < this.preferredTabWidth){
36121                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36122             }
36123         }
36124     },
36125
36126     /**
36127      * Returns the number of tabs in this TabPanel.
36128      * @return {Number}
36129      */
36130      getCount : function(){
36131          return this.items.length;
36132      },
36133
36134     /**
36135      * Resizes all the tabs to the passed width
36136      * @param {Number} The new width
36137      */
36138     setTabWidth : function(width){
36139         this.currentTabWidth = width;
36140         for(var i = 0, len = this.items.length; i < len; i++) {
36141                 if(!this.items[i].isHidden()) {
36142                 this.items[i].setWidth(width);
36143             }
36144         }
36145     },
36146
36147     /**
36148      * Destroys this TabPanel
36149      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36150      */
36151     destroy : function(removeEl){
36152         Roo.EventManager.removeResizeListener(this.onResize, this);
36153         for(var i = 0, len = this.items.length; i < len; i++){
36154             this.items[i].purgeListeners();
36155         }
36156         if(removeEl === true){
36157             this.el.update("");
36158             this.el.remove();
36159         }
36160     },
36161     
36162     createStrip : function(container)
36163     {
36164         var strip = document.createElement("nav");
36165         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36166         container.appendChild(strip);
36167         return strip;
36168     },
36169     
36170     createStripList : function(strip)
36171     {
36172         // div wrapper for retard IE
36173         // returns the "tr" element.
36174         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36175         //'<div class="x-tabs-strip-wrap">'+
36176           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36177           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36178         return strip.firstChild; //.firstChild.firstChild.firstChild;
36179     },
36180     createBody : function(container)
36181     {
36182         var body = document.createElement("div");
36183         Roo.id(body, "tab-body");
36184         //Roo.fly(body).addClass("x-tabs-body");
36185         Roo.fly(body).addClass("tab-content");
36186         container.appendChild(body);
36187         return body;
36188     },
36189     createItemBody :function(bodyEl, id){
36190         var body = Roo.getDom(id);
36191         if(!body){
36192             body = document.createElement("div");
36193             body.id = id;
36194         }
36195         //Roo.fly(body).addClass("x-tabs-item-body");
36196         Roo.fly(body).addClass("tab-pane");
36197          bodyEl.insertBefore(body, bodyEl.firstChild);
36198         return body;
36199     },
36200     /** @private */
36201     createStripElements :  function(stripEl, text, closable, tpl)
36202     {
36203         var td = document.createElement("li"); // was td..
36204         
36205         
36206         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36207         
36208         
36209         stripEl.appendChild(td);
36210         /*if(closable){
36211             td.className = "x-tabs-closable";
36212             if(!this.closeTpl){
36213                 this.closeTpl = new Roo.Template(
36214                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36215                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36216                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36217                 );
36218             }
36219             var el = this.closeTpl.overwrite(td, {"text": text});
36220             var close = el.getElementsByTagName("div")[0];
36221             var inner = el.getElementsByTagName("em")[0];
36222             return {"el": el, "close": close, "inner": inner};
36223         } else {
36224         */
36225         // not sure what this is..
36226 //            if(!this.tabTpl){
36227                 //this.tabTpl = new Roo.Template(
36228                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36229                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36230                 //);
36231 //                this.tabTpl = new Roo.Template(
36232 //                   '<a href="#">' +
36233 //                   '<span unselectable="on"' +
36234 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36235 //                            ' >{text}</span></a>'
36236 //                );
36237 //                
36238 //            }
36239
36240
36241             var template = tpl || this.tabTpl || false;
36242             
36243             if(!template){
36244                 
36245                 template = new Roo.Template(
36246                    '<a href="#">' +
36247                    '<span unselectable="on"' +
36248                             (this.disableTooltips ? '' : ' title="{text}"') +
36249                             ' >{text}</span></a>'
36250                 );
36251             }
36252             
36253             switch (typeof(template)) {
36254                 case 'object' :
36255                     break;
36256                 case 'string' :
36257                     template = new Roo.Template(template);
36258                     break;
36259                 default :
36260                     break;
36261             }
36262             
36263             var el = template.overwrite(td, {"text": text});
36264             
36265             var inner = el.getElementsByTagName("span")[0];
36266             
36267             return {"el": el, "inner": inner};
36268             
36269     }
36270         
36271     
36272 });
36273
36274 /**
36275  * @class Roo.TabPanelItem
36276  * @extends Roo.util.Observable
36277  * Represents an individual item (tab plus body) in a TabPanel.
36278  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36279  * @param {String} id The id of this TabPanelItem
36280  * @param {String} text The text for the tab of this TabPanelItem
36281  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36282  */
36283 Roo.bootstrap.panel.TabItem = function(config){
36284     /**
36285      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36286      * @type Roo.TabPanel
36287      */
36288     this.tabPanel = config.panel;
36289     /**
36290      * The id for this TabPanelItem
36291      * @type String
36292      */
36293     this.id = config.id;
36294     /** @private */
36295     this.disabled = false;
36296     /** @private */
36297     this.text = config.text;
36298     /** @private */
36299     this.loaded = false;
36300     this.closable = config.closable;
36301
36302     /**
36303      * The body element for this TabPanelItem.
36304      * @type Roo.Element
36305      */
36306     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36307     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36308     this.bodyEl.setStyle("display", "block");
36309     this.bodyEl.setStyle("zoom", "1");
36310     //this.hideAction();
36311
36312     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36313     /** @private */
36314     this.el = Roo.get(els.el);
36315     this.inner = Roo.get(els.inner, true);
36316     this.textEl = Roo.get(this.el.dom.firstChild, true);
36317     this.pnode = Roo.get(els.el.parentNode, true);
36318     this.el.on("mousedown", this.onTabMouseDown, this);
36319     this.el.on("click", this.onTabClick, this);
36320     /** @private */
36321     if(config.closable){
36322         var c = Roo.get(els.close, true);
36323         c.dom.title = this.closeText;
36324         c.addClassOnOver("close-over");
36325         c.on("click", this.closeClick, this);
36326      }
36327
36328     this.addEvents({
36329          /**
36330          * @event activate
36331          * Fires when this tab becomes the active tab.
36332          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36333          * @param {Roo.TabPanelItem} this
36334          */
36335         "activate": true,
36336         /**
36337          * @event beforeclose
36338          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36339          * @param {Roo.TabPanelItem} this
36340          * @param {Object} e Set cancel to true on this object to cancel the close.
36341          */
36342         "beforeclose": true,
36343         /**
36344          * @event close
36345          * Fires when this tab is closed.
36346          * @param {Roo.TabPanelItem} this
36347          */
36348          "close": true,
36349         /**
36350          * @event deactivate
36351          * Fires when this tab is no longer the active tab.
36352          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36353          * @param {Roo.TabPanelItem} this
36354          */
36355          "deactivate" : true
36356     });
36357     this.hidden = false;
36358
36359     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36360 };
36361
36362 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36363            {
36364     purgeListeners : function(){
36365        Roo.util.Observable.prototype.purgeListeners.call(this);
36366        this.el.removeAllListeners();
36367     },
36368     /**
36369      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36370      */
36371     show : function(){
36372         this.pnode.addClass("active");
36373         this.showAction();
36374         if(Roo.isOpera){
36375             this.tabPanel.stripWrap.repaint();
36376         }
36377         this.fireEvent("activate", this.tabPanel, this);
36378     },
36379
36380     /**
36381      * Returns true if this tab is the active tab.
36382      * @return {Boolean}
36383      */
36384     isActive : function(){
36385         return this.tabPanel.getActiveTab() == this;
36386     },
36387
36388     /**
36389      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36390      */
36391     hide : function(){
36392         this.pnode.removeClass("active");
36393         this.hideAction();
36394         this.fireEvent("deactivate", this.tabPanel, this);
36395     },
36396
36397     hideAction : function(){
36398         this.bodyEl.hide();
36399         this.bodyEl.setStyle("position", "absolute");
36400         this.bodyEl.setLeft("-20000px");
36401         this.bodyEl.setTop("-20000px");
36402     },
36403
36404     showAction : function(){
36405         this.bodyEl.setStyle("position", "relative");
36406         this.bodyEl.setTop("");
36407         this.bodyEl.setLeft("");
36408         this.bodyEl.show();
36409     },
36410
36411     /**
36412      * Set the tooltip for the tab.
36413      * @param {String} tooltip The tab's tooltip
36414      */
36415     setTooltip : function(text){
36416         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36417             this.textEl.dom.qtip = text;
36418             this.textEl.dom.removeAttribute('title');
36419         }else{
36420             this.textEl.dom.title = text;
36421         }
36422     },
36423
36424     onTabClick : function(e){
36425         e.preventDefault();
36426         this.tabPanel.activate(this.id);
36427     },
36428
36429     onTabMouseDown : function(e){
36430         e.preventDefault();
36431         this.tabPanel.activate(this.id);
36432     },
36433 /*
36434     getWidth : function(){
36435         return this.inner.getWidth();
36436     },
36437
36438     setWidth : function(width){
36439         var iwidth = width - this.pnode.getPadding("lr");
36440         this.inner.setWidth(iwidth);
36441         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36442         this.pnode.setWidth(width);
36443     },
36444 */
36445     /**
36446      * Show or hide the tab
36447      * @param {Boolean} hidden True to hide or false to show.
36448      */
36449     setHidden : function(hidden){
36450         this.hidden = hidden;
36451         this.pnode.setStyle("display", hidden ? "none" : "");
36452     },
36453
36454     /**
36455      * Returns true if this tab is "hidden"
36456      * @return {Boolean}
36457      */
36458     isHidden : function(){
36459         return this.hidden;
36460     },
36461
36462     /**
36463      * Returns the text for this tab
36464      * @return {String}
36465      */
36466     getText : function(){
36467         return this.text;
36468     },
36469     /*
36470     autoSize : function(){
36471         //this.el.beginMeasure();
36472         this.textEl.setWidth(1);
36473         /*
36474          *  #2804 [new] Tabs in Roojs
36475          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36476          */
36477         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36478         //this.el.endMeasure();
36479     //},
36480
36481     /**
36482      * Sets the text for the tab (Note: this also sets the tooltip text)
36483      * @param {String} text The tab's text and tooltip
36484      */
36485     setText : function(text){
36486         this.text = text;
36487         this.textEl.update(text);
36488         this.setTooltip(text);
36489         //if(!this.tabPanel.resizeTabs){
36490         //    this.autoSize();
36491         //}
36492     },
36493     /**
36494      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36495      */
36496     activate : function(){
36497         this.tabPanel.activate(this.id);
36498     },
36499
36500     /**
36501      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36502      */
36503     disable : function(){
36504         if(this.tabPanel.active != this){
36505             this.disabled = true;
36506             this.pnode.addClass("disabled");
36507         }
36508     },
36509
36510     /**
36511      * Enables this TabPanelItem if it was previously disabled.
36512      */
36513     enable : function(){
36514         this.disabled = false;
36515         this.pnode.removeClass("disabled");
36516     },
36517
36518     /**
36519      * Sets the content for this TabPanelItem.
36520      * @param {String} content The content
36521      * @param {Boolean} loadScripts true to look for and load scripts
36522      */
36523     setContent : function(content, loadScripts){
36524         this.bodyEl.update(content, loadScripts);
36525     },
36526
36527     /**
36528      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36529      * @return {Roo.UpdateManager} The UpdateManager
36530      */
36531     getUpdateManager : function(){
36532         return this.bodyEl.getUpdateManager();
36533     },
36534
36535     /**
36536      * Set a URL to be used to load the content for this TabPanelItem.
36537      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36538      * @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)
36539      * @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)
36540      * @return {Roo.UpdateManager} The UpdateManager
36541      */
36542     setUrl : function(url, params, loadOnce){
36543         if(this.refreshDelegate){
36544             this.un('activate', this.refreshDelegate);
36545         }
36546         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36547         this.on("activate", this.refreshDelegate);
36548         return this.bodyEl.getUpdateManager();
36549     },
36550
36551     /** @private */
36552     _handleRefresh : function(url, params, loadOnce){
36553         if(!loadOnce || !this.loaded){
36554             var updater = this.bodyEl.getUpdateManager();
36555             updater.update(url, params, this._setLoaded.createDelegate(this));
36556         }
36557     },
36558
36559     /**
36560      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36561      *   Will fail silently if the setUrl method has not been called.
36562      *   This does not activate the panel, just updates its content.
36563      */
36564     refresh : function(){
36565         if(this.refreshDelegate){
36566            this.loaded = false;
36567            this.refreshDelegate();
36568         }
36569     },
36570
36571     /** @private */
36572     _setLoaded : function(){
36573         this.loaded = true;
36574     },
36575
36576     /** @private */
36577     closeClick : function(e){
36578         var o = {};
36579         e.stopEvent();
36580         this.fireEvent("beforeclose", this, o);
36581         if(o.cancel !== true){
36582             this.tabPanel.removeTab(this.id);
36583         }
36584     },
36585     /**
36586      * The text displayed in the tooltip for the close icon.
36587      * @type String
36588      */
36589     closeText : "Close this tab"
36590 });