compile
[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         
7581         items.each(function(f){
7582             
7583             if(f.validate()){
7584                 return;
7585             }
7586             
7587             valid = false;
7588
7589             if(!target){
7590                 target = f;
7591             }
7592            
7593         });
7594         
7595         if(this.errPopover && !valid){
7596             this.showErrPopover(target);
7597         }
7598         
7599         return valid;
7600     },
7601     
7602     showErrPopover : function(target)
7603     {
7604         if(!this.errPopover){
7605             return;
7606         }
7607         
7608         var oIndex = target.el.getStyle('z-index');
7609         
7610         target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7611         
7612         target.el.addClass('roo-invalid-outline');
7613         
7614         target.inputEl().focus();
7615         
7616         var fadeout = function(){
7617             
7618             target.inputEl().un('blur', fadeout);
7619             target.inputEl().un('keyup', fadeout);
7620             
7621             target.el.setStyle('z-index', oIndex);
7622         
7623             target.el.removeClass('roo-invalid-outline');
7624             
7625         }
7626         
7627         target.inputEl().on('blur', fadeout, target);
7628         target.inputEl().on('keyup', fadeout, target);
7629         
7630           
7631     },
7632     
7633     /**
7634      * Returns true if any fields in this form have changed since their original load.
7635      * @return Boolean
7636      */
7637     isDirty : function(){
7638         var dirty = false;
7639         var items = this.getItems();
7640         items.each(function(f){
7641            if(f.isDirty()){
7642                dirty = true;
7643                return false;
7644            }
7645            return true;
7646         });
7647         return dirty;
7648     },
7649      /**
7650      * Performs a predefined action (submit or load) or custom actions you define on this form.
7651      * @param {String} actionName The name of the action type
7652      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7653      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7654      * accept other config options):
7655      * <pre>
7656 Property          Type             Description
7657 ----------------  ---------------  ----------------------------------------------------------------------------------
7658 url               String           The url for the action (defaults to the form's url)
7659 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7660 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7661 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7662                                    validate the form on the client (defaults to false)
7663      * </pre>
7664      * @return {BasicForm} this
7665      */
7666     doAction : function(action, options){
7667         if(typeof action == 'string'){
7668             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7669         }
7670         if(this.fireEvent('beforeaction', this, action) !== false){
7671             this.beforeAction(action);
7672             action.run.defer(100, action);
7673         }
7674         return this;
7675     },
7676
7677     // private
7678     beforeAction : function(action){
7679         var o = action.options;
7680
7681         if(this.loadMask){
7682             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7683         }
7684         // not really supported yet.. ??
7685
7686         //if(this.waitMsgTarget === true){
7687         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7688         //}else if(this.waitMsgTarget){
7689         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7690         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7691         //}else {
7692         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7693        // }
7694
7695     },
7696
7697     // private
7698     afterAction : function(action, success){
7699         this.activeAction = null;
7700         var o = action.options;
7701
7702         //if(this.waitMsgTarget === true){
7703             this.el.unmask();
7704         //}else if(this.waitMsgTarget){
7705         //    this.waitMsgTarget.unmask();
7706         //}else{
7707         //    Roo.MessageBox.updateProgress(1);
7708         //    Roo.MessageBox.hide();
7709        // }
7710         //
7711         if(success){
7712             if(o.reset){
7713                 this.reset();
7714             }
7715             Roo.callback(o.success, o.scope, [this, action]);
7716             this.fireEvent('actioncomplete', this, action);
7717
7718         }else{
7719
7720             // failure condition..
7721             // we have a scenario where updates need confirming.
7722             // eg. if a locking scenario exists..
7723             // we look for { errors : { needs_confirm : true }} in the response.
7724             if (
7725                 (typeof(action.result) != 'undefined')  &&
7726                 (typeof(action.result.errors) != 'undefined')  &&
7727                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7728            ){
7729                 var _t = this;
7730                 Roo.log("not supported yet");
7731                  /*
7732
7733                 Roo.MessageBox.confirm(
7734                     "Change requires confirmation",
7735                     action.result.errorMsg,
7736                     function(r) {
7737                         if (r != 'yes') {
7738                             return;
7739                         }
7740                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7741                     }
7742
7743                 );
7744                 */
7745
7746
7747                 return;
7748             }
7749
7750             Roo.callback(o.failure, o.scope, [this, action]);
7751             // show an error message if no failed handler is set..
7752             if (!this.hasListener('actionfailed')) {
7753                 Roo.log("need to add dialog support");
7754                 /*
7755                 Roo.MessageBox.alert("Error",
7756                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7757                         action.result.errorMsg :
7758                         "Saving Failed, please check your entries or try again"
7759                 );
7760                 */
7761             }
7762
7763             this.fireEvent('actionfailed', this, action);
7764         }
7765
7766     },
7767     /**
7768      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7769      * @param {String} id The value to search for
7770      * @return Field
7771      */
7772     findField : function(id){
7773         var items = this.getItems();
7774         var field = items.get(id);
7775         if(!field){
7776              items.each(function(f){
7777                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7778                     field = f;
7779                     return false;
7780                 }
7781                 return true;
7782             });
7783         }
7784         return field || null;
7785     },
7786      /**
7787      * Mark fields in this form invalid in bulk.
7788      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7789      * @return {BasicForm} this
7790      */
7791     markInvalid : function(errors){
7792         if(errors instanceof Array){
7793             for(var i = 0, len = errors.length; i < len; i++){
7794                 var fieldError = errors[i];
7795                 var f = this.findField(fieldError.id);
7796                 if(f){
7797                     f.markInvalid(fieldError.msg);
7798                 }
7799             }
7800         }else{
7801             var field, id;
7802             for(id in errors){
7803                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7804                     field.markInvalid(errors[id]);
7805                 }
7806             }
7807         }
7808         //Roo.each(this.childForms || [], function (f) {
7809         //    f.markInvalid(errors);
7810         //});
7811
7812         return this;
7813     },
7814
7815     /**
7816      * Set values for fields in this form in bulk.
7817      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7818      * @return {BasicForm} this
7819      */
7820     setValues : function(values){
7821         if(values instanceof Array){ // array of objects
7822             for(var i = 0, len = values.length; i < len; i++){
7823                 var v = values[i];
7824                 var f = this.findField(v.id);
7825                 if(f){
7826                     f.setValue(v.value);
7827                     if(this.trackResetOnLoad){
7828                         f.originalValue = f.getValue();
7829                     }
7830                 }
7831             }
7832         }else{ // object hash
7833             var field, id;
7834             for(id in values){
7835                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7836
7837                     if (field.setFromData &&
7838                         field.valueField &&
7839                         field.displayField &&
7840                         // combos' with local stores can
7841                         // be queried via setValue()
7842                         // to set their value..
7843                         (field.store && !field.store.isLocal)
7844                         ) {
7845                         // it's a combo
7846                         var sd = { };
7847                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7848                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7849                         field.setFromData(sd);
7850
7851                     } else {
7852                         field.setValue(values[id]);
7853                     }
7854
7855
7856                     if(this.trackResetOnLoad){
7857                         field.originalValue = field.getValue();
7858                     }
7859                 }
7860             }
7861         }
7862
7863         //Roo.each(this.childForms || [], function (f) {
7864         //    f.setValues(values);
7865         //});
7866
7867         return this;
7868     },
7869
7870     /**
7871      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7872      * they are returned as an array.
7873      * @param {Boolean} asString
7874      * @return {Object}
7875      */
7876     getValues : function(asString){
7877         //if (this.childForms) {
7878             // copy values from the child forms
7879         //    Roo.each(this.childForms, function (f) {
7880         //        this.setValues(f.getValues());
7881         //    }, this);
7882         //}
7883
7884
7885
7886         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7887         if(asString === true){
7888             return fs;
7889         }
7890         return Roo.urlDecode(fs);
7891     },
7892
7893     /**
7894      * Returns the fields in this form as an object with key/value pairs.
7895      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7896      * @return {Object}
7897      */
7898     getFieldValues : function(with_hidden)
7899     {
7900         var items = this.getItems();
7901         var ret = {};
7902         items.each(function(f){
7903             if (!f.getName()) {
7904                 return;
7905             }
7906             var v = f.getValue();
7907             if (f.inputType =='radio') {
7908                 if (typeof(ret[f.getName()]) == 'undefined') {
7909                     ret[f.getName()] = ''; // empty..
7910                 }
7911
7912                 if (!f.el.dom.checked) {
7913                     return;
7914
7915                 }
7916                 v = f.el.dom.value;
7917
7918             }
7919
7920             // not sure if this supported any more..
7921             if ((typeof(v) == 'object') && f.getRawValue) {
7922                 v = f.getRawValue() ; // dates..
7923             }
7924             // combo boxes where name != hiddenName...
7925             if (f.name != f.getName()) {
7926                 ret[f.name] = f.getRawValue();
7927             }
7928             ret[f.getName()] = v;
7929         });
7930
7931         return ret;
7932     },
7933
7934     /**
7935      * Clears all invalid messages in this form.
7936      * @return {BasicForm} this
7937      */
7938     clearInvalid : function(){
7939         var items = this.getItems();
7940
7941         items.each(function(f){
7942            f.clearInvalid();
7943         });
7944
7945
7946
7947         return this;
7948     },
7949
7950     /**
7951      * Resets this form.
7952      * @return {BasicForm} this
7953      */
7954     reset : function(){
7955         var items = this.getItems();
7956         items.each(function(f){
7957             f.reset();
7958         });
7959
7960         Roo.each(this.childForms || [], function (f) {
7961             f.reset();
7962         });
7963
7964
7965         return this;
7966     },
7967     getItems : function()
7968     {
7969         var r=new Roo.util.MixedCollection(false, function(o){
7970             return o.id || (o.id = Roo.id());
7971         });
7972         var iter = function(el) {
7973             if (el.inputEl) {
7974                 r.add(el);
7975             }
7976             if (!el.items) {
7977                 return;
7978             }
7979             Roo.each(el.items,function(e) {
7980                 iter(e);
7981             });
7982
7983
7984         };
7985
7986         iter(this);
7987         return r;
7988
7989
7990
7991
7992     }
7993
7994 });
7995 /*
7996  * Based on:
7997  * Ext JS Library 1.1.1
7998  * Copyright(c) 2006-2007, Ext JS, LLC.
7999  *
8000  * Originally Released Under LGPL - original licence link has changed is not relivant.
8001  *
8002  * Fork - LGPL
8003  * <script type="text/javascript">
8004  */
8005 /**
8006  * @class Roo.form.VTypes
8007  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8008  * @singleton
8009  */
8010 Roo.form.VTypes = function(){
8011     // closure these in so they are only created once.
8012     var alpha = /^[a-zA-Z_]+$/;
8013     var alphanum = /^[a-zA-Z0-9_]+$/;
8014     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8015     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8016
8017     // All these messages and functions are configurable
8018     return {
8019         /**
8020          * The function used to validate email addresses
8021          * @param {String} value The email address
8022          */
8023         'email' : function(v){
8024             return email.test(v);
8025         },
8026         /**
8027          * The error text to display when the email validation function returns false
8028          * @type String
8029          */
8030         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8031         /**
8032          * The keystroke filter mask to be applied on email input
8033          * @type RegExp
8034          */
8035         'emailMask' : /[a-z0-9_\.\-@]/i,
8036
8037         /**
8038          * The function used to validate URLs
8039          * @param {String} value The URL
8040          */
8041         'url' : function(v){
8042             return url.test(v);
8043         },
8044         /**
8045          * The error text to display when the url validation function returns false
8046          * @type String
8047          */
8048         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8049         
8050         /**
8051          * The function used to validate alpha values
8052          * @param {String} value The value
8053          */
8054         'alpha' : function(v){
8055             return alpha.test(v);
8056         },
8057         /**
8058          * The error text to display when the alpha validation function returns false
8059          * @type String
8060          */
8061         'alphaText' : 'This field should only contain letters and _',
8062         /**
8063          * The keystroke filter mask to be applied on alpha input
8064          * @type RegExp
8065          */
8066         'alphaMask' : /[a-z_]/i,
8067
8068         /**
8069          * The function used to validate alphanumeric values
8070          * @param {String} value The value
8071          */
8072         'alphanum' : function(v){
8073             return alphanum.test(v);
8074         },
8075         /**
8076          * The error text to display when the alphanumeric validation function returns false
8077          * @type String
8078          */
8079         'alphanumText' : 'This field should only contain letters, numbers and _',
8080         /**
8081          * The keystroke filter mask to be applied on alphanumeric input
8082          * @type RegExp
8083          */
8084         'alphanumMask' : /[a-z0-9_]/i
8085     };
8086 }();/*
8087  * - LGPL
8088  *
8089  * Input
8090  * 
8091  */
8092
8093 /**
8094  * @class Roo.bootstrap.Input
8095  * @extends Roo.bootstrap.Component
8096  * Bootstrap Input class
8097  * @cfg {Boolean} disabled is it disabled
8098  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8099  * @cfg {String} name name of the input
8100  * @cfg {string} fieldLabel - the label associated
8101  * @cfg {string} placeholder - placeholder to put in text.
8102  * @cfg {string}  before - input group add on before
8103  * @cfg {string} after - input group add on after
8104  * @cfg {string} size - (lg|sm) or leave empty..
8105  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8106  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8107  * @cfg {Number} md colspan out of 12 for computer-sized screens
8108  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8109  * @cfg {string} value default value of the input
8110  * @cfg {Number} labelWidth set the width of label (0-12)
8111  * @cfg {String} labelAlign (top|left)
8112  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8113  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8114  * @cfg {String} indicatorpos (left|right) default left
8115
8116  * @cfg {String} align (left|center|right) Default left
8117  * @cfg {Boolean} forceFeedback (true|false) Default false
8118  * 
8119  * 
8120  * 
8121  * 
8122  * @constructor
8123  * Create a new Input
8124  * @param {Object} config The config object
8125  */
8126
8127 Roo.bootstrap.Input = function(config){
8128     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8129    
8130         this.addEvents({
8131             /**
8132              * @event focus
8133              * Fires when this field receives input focus.
8134              * @param {Roo.form.Field} this
8135              */
8136             focus : true,
8137             /**
8138              * @event blur
8139              * Fires when this field loses input focus.
8140              * @param {Roo.form.Field} this
8141              */
8142             blur : true,
8143             /**
8144              * @event specialkey
8145              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8146              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8147              * @param {Roo.form.Field} this
8148              * @param {Roo.EventObject} e The event object
8149              */
8150             specialkey : true,
8151             /**
8152              * @event change
8153              * Fires just before the field blurs if the field value has changed.
8154              * @param {Roo.form.Field} this
8155              * @param {Mixed} newValue The new value
8156              * @param {Mixed} oldValue The original value
8157              */
8158             change : true,
8159             /**
8160              * @event invalid
8161              * Fires after the field has been marked as invalid.
8162              * @param {Roo.form.Field} this
8163              * @param {String} msg The validation message
8164              */
8165             invalid : true,
8166             /**
8167              * @event valid
8168              * Fires after the field has been validated with no errors.
8169              * @param {Roo.form.Field} this
8170              */
8171             valid : true,
8172              /**
8173              * @event keyup
8174              * Fires after the key up
8175              * @param {Roo.form.Field} this
8176              * @param {Roo.EventObject}  e The event Object
8177              */
8178             keyup : true
8179         });
8180 };
8181
8182 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8183      /**
8184      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8185       automatic validation (defaults to "keyup").
8186      */
8187     validationEvent : "keyup",
8188      /**
8189      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8190      */
8191     validateOnBlur : true,
8192     /**
8193      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8194      */
8195     validationDelay : 250,
8196      /**
8197      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8198      */
8199     focusClass : "x-form-focus",  // not needed???
8200     
8201        
8202     /**
8203      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8204      */
8205     invalidClass : "has-warning",
8206     
8207     /**
8208      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8209      */
8210     validClass : "has-success",
8211     
8212     /**
8213      * @cfg {Boolean} hasFeedback (true|false) default true
8214      */
8215     hasFeedback : true,
8216     
8217     /**
8218      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8219      */
8220     invalidFeedbackClass : "glyphicon-warning-sign",
8221     
8222     /**
8223      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8224      */
8225     validFeedbackClass : "glyphicon-ok",
8226     
8227     /**
8228      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8229      */
8230     selectOnFocus : false,
8231     
8232      /**
8233      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8234      */
8235     maskRe : null,
8236        /**
8237      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8238      */
8239     vtype : null,
8240     
8241       /**
8242      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8243      */
8244     disableKeyFilter : false,
8245     
8246        /**
8247      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8248      */
8249     disabled : false,
8250      /**
8251      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8252      */
8253     allowBlank : true,
8254     /**
8255      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8256      */
8257     blankText : "This field is required",
8258     
8259      /**
8260      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8261      */
8262     minLength : 0,
8263     /**
8264      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8265      */
8266     maxLength : Number.MAX_VALUE,
8267     /**
8268      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8269      */
8270     minLengthText : "The minimum length for this field is {0}",
8271     /**
8272      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8273      */
8274     maxLengthText : "The maximum length for this field is {0}",
8275   
8276     
8277     /**
8278      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8279      * If available, this function will be called only after the basic validators all return true, and will be passed the
8280      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8281      */
8282     validator : null,
8283     /**
8284      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8285      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8286      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8287      */
8288     regex : null,
8289     /**
8290      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8291      */
8292     regexText : "",
8293     
8294     autocomplete: false,
8295     
8296     
8297     fieldLabel : '',
8298     inputType : 'text',
8299     
8300     name : false,
8301     placeholder: false,
8302     before : false,
8303     after : false,
8304     size : false,
8305     hasFocus : false,
8306     preventMark: false,
8307     isFormField : true,
8308     value : '',
8309     labelWidth : 2,
8310     labelAlign : false,
8311     readOnly : false,
8312     align : false,
8313     formatedValue : false,
8314     forceFeedback : false,
8315     
8316     indicatorpos : 'left',
8317     
8318     parentLabelAlign : function()
8319     {
8320         var parent = this;
8321         while (parent.parent()) {
8322             parent = parent.parent();
8323             if (typeof(parent.labelAlign) !='undefined') {
8324                 return parent.labelAlign;
8325             }
8326         }
8327         return 'left';
8328         
8329     },
8330     
8331     getAutoCreate : function()
8332     {
8333         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8334         
8335         var id = Roo.id();
8336         
8337         var cfg = {};
8338         
8339         if(this.inputType != 'hidden'){
8340             cfg.cls = 'form-group' //input-group
8341         }
8342         
8343         var input =  {
8344             tag: 'input',
8345             id : id,
8346             type : this.inputType,
8347             value : this.value,
8348             cls : 'form-control',
8349             placeholder : this.placeholder || '',
8350             autocomplete : this.autocomplete || 'new-password'
8351         };
8352         
8353         if(this.align){
8354             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8355         }
8356         
8357         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8358             input.maxLength = this.maxLength;
8359         }
8360         
8361         if (this.disabled) {
8362             input.disabled=true;
8363         }
8364         
8365         if (this.readOnly) {
8366             input.readonly=true;
8367         }
8368         
8369         if (this.name) {
8370             input.name = this.name;
8371         }
8372         
8373         if (this.size) {
8374             input.cls += ' input-' + this.size;
8375         }
8376         
8377         var settings=this;
8378         ['xs','sm','md','lg'].map(function(size){
8379             if (settings[size]) {
8380                 cfg.cls += ' col-' + size + '-' + settings[size];
8381             }
8382         });
8383         
8384         var inputblock = input;
8385         
8386         var feedback = {
8387             tag: 'span',
8388             cls: 'glyphicon form-control-feedback'
8389         };
8390             
8391         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8392             
8393             inputblock = {
8394                 cls : 'has-feedback',
8395                 cn :  [
8396                     input,
8397                     feedback
8398                 ] 
8399             };  
8400         }
8401         
8402         if (this.before || this.after) {
8403             
8404             inputblock = {
8405                 cls : 'input-group',
8406                 cn :  [] 
8407             };
8408             
8409             if (this.before && typeof(this.before) == 'string') {
8410                 
8411                 inputblock.cn.push({
8412                     tag :'span',
8413                     cls : 'roo-input-before input-group-addon',
8414                     html : this.before
8415                 });
8416             }
8417             if (this.before && typeof(this.before) == 'object') {
8418                 this.before = Roo.factory(this.before);
8419                 
8420                 inputblock.cn.push({
8421                     tag :'span',
8422                     cls : 'roo-input-before input-group-' +
8423                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8424                 });
8425             }
8426             
8427             inputblock.cn.push(input);
8428             
8429             if (this.after && typeof(this.after) == 'string') {
8430                 inputblock.cn.push({
8431                     tag :'span',
8432                     cls : 'roo-input-after input-group-addon',
8433                     html : this.after
8434                 });
8435             }
8436             if (this.after && typeof(this.after) == 'object') {
8437                 this.after = Roo.factory(this.after);
8438                 
8439                 inputblock.cn.push({
8440                     tag :'span',
8441                     cls : 'roo-input-after input-group-' +
8442                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8443                 });
8444             }
8445             
8446             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8447                 inputblock.cls += ' has-feedback';
8448                 inputblock.cn.push(feedback);
8449             }
8450         };
8451         
8452         if (align ==='left' && this.fieldLabel.length) {
8453             
8454             cfg.cn = [
8455                 {
8456                     tag : 'i',
8457                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8458                     tooltip : 'This field is required'
8459                 },
8460                 {
8461                     tag: 'label',
8462                     'for' :  id,
8463                     cls : 'control-label col-sm-' + this.labelWidth,
8464                     html : this.fieldLabel
8465
8466                 },
8467                 {
8468                     cls : "col-sm-" + (12 - this.labelWidth), 
8469                     cn: [
8470                         inputblock
8471                     ]
8472                 }
8473
8474             ];
8475             
8476             if(this.indicatorpos == 'right'){
8477                 cfg.cn = [
8478                     {
8479                         tag: 'label',
8480                         'for' :  id,
8481                         cls : 'control-label col-sm-' + this.labelWidth,
8482                         html : this.fieldLabel
8483
8484                     },
8485                     {
8486                         tag : 'i',
8487                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8488                         tooltip : 'This field is required'
8489                     },
8490                     {
8491                         cls : "col-sm-" + (12 - this.labelWidth), 
8492                         cn: [
8493                             inputblock
8494                         ]
8495                     }
8496
8497                 ];
8498             }
8499             
8500         } else if ( this.fieldLabel.length) {
8501                 
8502             cfg.cn = [
8503                 {
8504                     tag : 'i',
8505                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8506                     tooltip : 'This field is required'
8507                 },
8508                 {
8509                     tag: 'label',
8510                    //cls : 'input-group-addon',
8511                     html : this.fieldLabel
8512
8513                 },
8514
8515                inputblock
8516
8517            ];
8518            
8519            if(this.indicatorpos == 'right'){
8520                 
8521                 cfg.cn = [
8522                     {
8523                         tag: 'label',
8524                        //cls : 'input-group-addon',
8525                         html : this.fieldLabel
8526
8527                     },
8528                     {
8529                         tag : 'i',
8530                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8531                         tooltip : 'This field is required'
8532                     },
8533
8534                    inputblock
8535
8536                ];
8537
8538             }
8539
8540         } else {
8541             
8542             cfg.cn = [
8543
8544                     inputblock
8545
8546             ];
8547                 
8548                 
8549         };
8550         
8551         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8552            cfg.cls += ' navbar-form';
8553         }
8554         
8555         if (this.parentType === 'NavGroup') {
8556            cfg.cls += ' navbar-form';
8557            cfg.tag = 'li';
8558         }
8559         
8560         return cfg;
8561         
8562     },
8563     /**
8564      * return the real input element.
8565      */
8566     inputEl: function ()
8567     {
8568         return this.el.select('input.form-control',true).first();
8569     },
8570     
8571     tooltipEl : function()
8572     {
8573         return this.inputEl();
8574     },
8575     
8576     indicatorEl : function()
8577     {
8578         var indicator = this.el.select('i.roo-required-indicator',true).first();
8579         
8580         if(!indicator){
8581             return false;
8582         }
8583         
8584         return indicator;
8585         
8586     },
8587     
8588     setDisabled : function(v)
8589     {
8590         var i  = this.inputEl().dom;
8591         if (!v) {
8592             i.removeAttribute('disabled');
8593             return;
8594             
8595         }
8596         i.setAttribute('disabled','true');
8597     },
8598     initEvents : function()
8599     {
8600           
8601         this.inputEl().on("keydown" , this.fireKey,  this);
8602         this.inputEl().on("focus", this.onFocus,  this);
8603         this.inputEl().on("blur", this.onBlur,  this);
8604         
8605         this.inputEl().relayEvent('keyup', this);
8606         
8607         this.indicator = this.indicatorEl();
8608         
8609         if(this.indicator){
8610             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8611             this.indicator.hide();
8612         }
8613  
8614         // reference to original value for reset
8615         this.originalValue = this.getValue();
8616         //Roo.form.TextField.superclass.initEvents.call(this);
8617         if(this.validationEvent == 'keyup'){
8618             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8619             this.inputEl().on('keyup', this.filterValidation, this);
8620         }
8621         else if(this.validationEvent !== false){
8622             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8623         }
8624         
8625         if(this.selectOnFocus){
8626             this.on("focus", this.preFocus, this);
8627             
8628         }
8629         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8630             this.inputEl().on("keypress", this.filterKeys, this);
8631         } else {
8632             this.inputEl().relayEvent('keypress', this);
8633         }
8634        /* if(this.grow){
8635             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8636             this.el.on("click", this.autoSize,  this);
8637         }
8638         */
8639         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8640             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8641         }
8642         
8643         if (typeof(this.before) == 'object') {
8644             this.before.render(this.el.select('.roo-input-before',true).first());
8645         }
8646         if (typeof(this.after) == 'object') {
8647             this.after.render(this.el.select('.roo-input-after',true).first());
8648         }
8649         
8650         
8651     },
8652     filterValidation : function(e){
8653         if(!e.isNavKeyPress()){
8654             this.validationTask.delay(this.validationDelay);
8655         }
8656     },
8657      /**
8658      * Validates the field value
8659      * @return {Boolean} True if the value is valid, else false
8660      */
8661     validate : function(){
8662         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8663         if(this.disabled || this.validateValue(this.getRawValue())){
8664             this.markValid();
8665             return true;
8666         }
8667         
8668         this.markInvalid();
8669         return false;
8670     },
8671     
8672     
8673     /**
8674      * Validates a value according to the field's validation rules and marks the field as invalid
8675      * if the validation fails
8676      * @param {Mixed} value The value to validate
8677      * @return {Boolean} True if the value is valid, else false
8678      */
8679     validateValue : function(value){
8680         if(value.length < 1)  { // if it's blank
8681             if(this.allowBlank){
8682                 return true;
8683             }
8684             return false;
8685         }
8686         
8687         if(value.length < this.minLength){
8688             return false;
8689         }
8690         if(value.length > this.maxLength){
8691             return false;
8692         }
8693         if(this.vtype){
8694             var vt = Roo.form.VTypes;
8695             if(!vt[this.vtype](value, this)){
8696                 return false;
8697             }
8698         }
8699         if(typeof this.validator == "function"){
8700             var msg = this.validator(value);
8701             if(msg !== true){
8702                 return false;
8703             }
8704         }
8705         
8706         if(this.regex && !this.regex.test(value)){
8707             return false;
8708         }
8709         
8710         return true;
8711     },
8712
8713     
8714     
8715      // private
8716     fireKey : function(e){
8717         //Roo.log('field ' + e.getKey());
8718         if(e.isNavKeyPress()){
8719             this.fireEvent("specialkey", this, e);
8720         }
8721     },
8722     focus : function (selectText){
8723         if(this.rendered){
8724             this.inputEl().focus();
8725             if(selectText === true){
8726                 this.inputEl().dom.select();
8727             }
8728         }
8729         return this;
8730     } ,
8731     
8732     onFocus : function(){
8733         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8734            // this.el.addClass(this.focusClass);
8735         }
8736         if(!this.hasFocus){
8737             this.hasFocus = true;
8738             this.startValue = this.getValue();
8739             this.fireEvent("focus", this);
8740         }
8741     },
8742     
8743     beforeBlur : Roo.emptyFn,
8744
8745     
8746     // private
8747     onBlur : function(){
8748         this.beforeBlur();
8749         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8750             //this.el.removeClass(this.focusClass);
8751         }
8752         this.hasFocus = false;
8753         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8754             this.validate();
8755         }
8756         var v = this.getValue();
8757         if(String(v) !== String(this.startValue)){
8758             this.fireEvent('change', this, v, this.startValue);
8759         }
8760         this.fireEvent("blur", this);
8761     },
8762     
8763     /**
8764      * Resets the current field value to the originally loaded value and clears any validation messages
8765      */
8766     reset : function(){
8767         this.setValue(this.originalValue);
8768         this.validate();
8769     },
8770      /**
8771      * Returns the name of the field
8772      * @return {Mixed} name The name field
8773      */
8774     getName: function(){
8775         return this.name;
8776     },
8777      /**
8778      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8779      * @return {Mixed} value The field value
8780      */
8781     getValue : function(){
8782         
8783         var v = this.inputEl().getValue();
8784         
8785         return v;
8786     },
8787     /**
8788      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8789      * @return {Mixed} value The field value
8790      */
8791     getRawValue : function(){
8792         var v = this.inputEl().getValue();
8793         
8794         return v;
8795     },
8796     
8797     /**
8798      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8799      * @param {Mixed} value The value to set
8800      */
8801     setRawValue : function(v){
8802         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8803     },
8804     
8805     selectText : function(start, end){
8806         var v = this.getRawValue();
8807         if(v.length > 0){
8808             start = start === undefined ? 0 : start;
8809             end = end === undefined ? v.length : end;
8810             var d = this.inputEl().dom;
8811             if(d.setSelectionRange){
8812                 d.setSelectionRange(start, end);
8813             }else if(d.createTextRange){
8814                 var range = d.createTextRange();
8815                 range.moveStart("character", start);
8816                 range.moveEnd("character", v.length-end);
8817                 range.select();
8818             }
8819         }
8820     },
8821     
8822     /**
8823      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8824      * @param {Mixed} value The value to set
8825      */
8826     setValue : function(v){
8827         this.value = v;
8828         if(this.rendered){
8829             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8830             this.validate();
8831         }
8832     },
8833     
8834     /*
8835     processValue : function(value){
8836         if(this.stripCharsRe){
8837             var newValue = value.replace(this.stripCharsRe, '');
8838             if(newValue !== value){
8839                 this.setRawValue(newValue);
8840                 return newValue;
8841             }
8842         }
8843         return value;
8844     },
8845   */
8846     preFocus : function(){
8847         
8848         if(this.selectOnFocus){
8849             this.inputEl().dom.select();
8850         }
8851     },
8852     filterKeys : function(e){
8853         var k = e.getKey();
8854         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8855             return;
8856         }
8857         var c = e.getCharCode(), cc = String.fromCharCode(c);
8858         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8859             return;
8860         }
8861         if(!this.maskRe.test(cc)){
8862             e.stopEvent();
8863         }
8864     },
8865      /**
8866      * Clear any invalid styles/messages for this field
8867      */
8868     clearInvalid : function(){
8869         
8870         if(!this.el || this.preventMark){ // not rendered
8871             return;
8872         }
8873         
8874         if(this.indicator){
8875             this.indicator.hide();
8876         }
8877         
8878         this.el.removeClass(this.invalidClass);
8879         
8880         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8881             
8882             var feedback = this.el.select('.form-control-feedback', true).first();
8883             
8884             if(feedback){
8885                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8886             }
8887             
8888         }
8889         
8890         this.fireEvent('valid', this);
8891     },
8892     
8893      /**
8894      * Mark this field as valid
8895      */
8896     markValid : function()
8897     {
8898         if(!this.el  || this.preventMark){ // not rendered
8899             return;
8900         }
8901         
8902         this.el.removeClass([this.invalidClass, this.validClass]);
8903         
8904         var feedback = this.el.select('.form-control-feedback', true).first();
8905             
8906         if(feedback){
8907             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8908         }
8909
8910         if(this.disabled){
8911             return;
8912         }
8913         
8914         if(this.allowBlank && !this.getRawValue().length){
8915             return;
8916         }
8917         
8918         if(this.indicator){
8919             this.indicator.hide();
8920         }
8921         
8922         this.el.addClass(this.validClass);
8923         
8924         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8925             
8926             var feedback = this.el.select('.form-control-feedback', true).first();
8927             
8928             if(feedback){
8929                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8930                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8931             }
8932             
8933         }
8934         
8935         this.fireEvent('valid', this);
8936     },
8937     
8938      /**
8939      * Mark this field as invalid
8940      * @param {String} msg The validation message
8941      */
8942     markInvalid : function(msg)
8943     {
8944         if(!this.el  || this.preventMark){ // not rendered
8945             return;
8946         }
8947         
8948         this.el.removeClass([this.invalidClass, this.validClass]);
8949         
8950         var feedback = this.el.select('.form-control-feedback', true).first();
8951             
8952         if(feedback){
8953             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8954         }
8955
8956         if(this.disabled){
8957             return;
8958         }
8959         
8960         if(this.allowBlank && !this.getRawValue().length){
8961             return;
8962         }
8963         
8964         if(this.indicator){
8965             this.indicator.show();
8966         }
8967         
8968         this.el.addClass(this.invalidClass);
8969         
8970         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8971             
8972             var feedback = this.el.select('.form-control-feedback', true).first();
8973             
8974             if(feedback){
8975                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8976                 
8977                 if(this.getValue().length || this.forceFeedback){
8978                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8979                 }
8980                 
8981             }
8982             
8983         }
8984         
8985         this.fireEvent('invalid', this, msg);
8986     },
8987     // private
8988     SafariOnKeyDown : function(event)
8989     {
8990         // this is a workaround for a password hang bug on chrome/ webkit.
8991         if (this.inputEl().dom.type != 'password') {
8992             return;
8993         }
8994         
8995         var isSelectAll = false;
8996         
8997         if(this.inputEl().dom.selectionEnd > 0){
8998             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8999         }
9000         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9001             event.preventDefault();
9002             this.setValue('');
9003             return;
9004         }
9005         
9006         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9007             
9008             event.preventDefault();
9009             // this is very hacky as keydown always get's upper case.
9010             //
9011             var cc = String.fromCharCode(event.getCharCode());
9012             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9013             
9014         }
9015     },
9016     adjustWidth : function(tag, w){
9017         tag = tag.toLowerCase();
9018         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9019             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9020                 if(tag == 'input'){
9021                     return w + 2;
9022                 }
9023                 if(tag == 'textarea'){
9024                     return w-2;
9025                 }
9026             }else if(Roo.isOpera){
9027                 if(tag == 'input'){
9028                     return w + 2;
9029                 }
9030                 if(tag == 'textarea'){
9031                     return w-2;
9032                 }
9033             }
9034         }
9035         return w;
9036     }
9037     
9038 });
9039
9040  
9041 /*
9042  * - LGPL
9043  *
9044  * Input
9045  * 
9046  */
9047
9048 /**
9049  * @class Roo.bootstrap.TextArea
9050  * @extends Roo.bootstrap.Input
9051  * Bootstrap TextArea class
9052  * @cfg {Number} cols Specifies the visible width of a text area
9053  * @cfg {Number} rows Specifies the visible number of lines in a text area
9054  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9055  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9056  * @cfg {string} html text
9057  * 
9058  * @constructor
9059  * Create a new TextArea
9060  * @param {Object} config The config object
9061  */
9062
9063 Roo.bootstrap.TextArea = function(config){
9064     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9065    
9066 };
9067
9068 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9069      
9070     cols : false,
9071     rows : 5,
9072     readOnly : false,
9073     warp : 'soft',
9074     resize : false,
9075     value: false,
9076     html: false,
9077     
9078     getAutoCreate : function(){
9079         
9080         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9081         
9082         var id = Roo.id();
9083         
9084         var cfg = {};
9085         
9086         var input =  {
9087             tag: 'textarea',
9088             id : id,
9089             warp : this.warp,
9090             rows : this.rows,
9091             value : this.value || '',
9092             html: this.html || '',
9093             cls : 'form-control',
9094             placeholder : this.placeholder || '' 
9095             
9096         };
9097         
9098         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9099             input.maxLength = this.maxLength;
9100         }
9101         
9102         if(this.resize){
9103             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9104         }
9105         
9106         if(this.cols){
9107             input.cols = this.cols;
9108         }
9109         
9110         if (this.readOnly) {
9111             input.readonly = true;
9112         }
9113         
9114         if (this.name) {
9115             input.name = this.name;
9116         }
9117         
9118         if (this.size) {
9119             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9120         }
9121         
9122         var settings=this;
9123         ['xs','sm','md','lg'].map(function(size){
9124             if (settings[size]) {
9125                 cfg.cls += ' col-' + size + '-' + settings[size];
9126             }
9127         });
9128         
9129         var inputblock = input;
9130         
9131         if(this.hasFeedback && !this.allowBlank){
9132             
9133             var feedback = {
9134                 tag: 'span',
9135                 cls: 'glyphicon form-control-feedback'
9136             };
9137
9138             inputblock = {
9139                 cls : 'has-feedback',
9140                 cn :  [
9141                     input,
9142                     feedback
9143                 ] 
9144             };  
9145         }
9146         
9147         
9148         if (this.before || this.after) {
9149             
9150             inputblock = {
9151                 cls : 'input-group',
9152                 cn :  [] 
9153             };
9154             if (this.before) {
9155                 inputblock.cn.push({
9156                     tag :'span',
9157                     cls : 'input-group-addon',
9158                     html : this.before
9159                 });
9160             }
9161             
9162             inputblock.cn.push(input);
9163             
9164             if(this.hasFeedback && !this.allowBlank){
9165                 inputblock.cls += ' has-feedback';
9166                 inputblock.cn.push(feedback);
9167             }
9168             
9169             if (this.after) {
9170                 inputblock.cn.push({
9171                     tag :'span',
9172                     cls : 'input-group-addon',
9173                     html : this.after
9174                 });
9175             }
9176             
9177         }
9178         
9179         if (align ==='left' && this.fieldLabel.length) {
9180 //                Roo.log("left and has label");
9181                 cfg.cn = [
9182                     
9183                     {
9184                         tag: 'label',
9185                         'for' :  id,
9186                         cls : 'control-label col-sm-' + this.labelWidth,
9187                         html : this.fieldLabel
9188                         
9189                     },
9190                     {
9191                         cls : "col-sm-" + (12 - this.labelWidth), 
9192                         cn: [
9193                             inputblock
9194                         ]
9195                     }
9196                     
9197                 ];
9198         } else if ( this.fieldLabel.length) {
9199 //                Roo.log(" label");
9200                  cfg.cn = [
9201                    
9202                     {
9203                         tag: 'label',
9204                         //cls : 'input-group-addon',
9205                         html : this.fieldLabel
9206                         
9207                     },
9208                     
9209                     inputblock
9210                     
9211                 ];
9212
9213         } else {
9214             
9215 //                   Roo.log(" no label && no align");
9216                 cfg.cn = [
9217                     
9218                         inputblock
9219                     
9220                 ];
9221                 
9222                 
9223         }
9224         
9225         if (this.disabled) {
9226             input.disabled=true;
9227         }
9228         
9229         return cfg;
9230         
9231     },
9232     /**
9233      * return the real textarea element.
9234      */
9235     inputEl: function ()
9236     {
9237         return this.el.select('textarea.form-control',true).first();
9238     },
9239     
9240     /**
9241      * Clear any invalid styles/messages for this field
9242      */
9243     clearInvalid : function()
9244     {
9245         
9246         if(!this.el || this.preventMark){ // not rendered
9247             return;
9248         }
9249         
9250         var label = this.el.select('label', true).first();
9251         var icon = this.el.select('i.fa-star', true).first();
9252         
9253         if(label && icon){
9254             icon.remove();
9255         }
9256         
9257         this.el.removeClass(this.invalidClass);
9258         
9259         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9260             
9261             var feedback = this.el.select('.form-control-feedback', true).first();
9262             
9263             if(feedback){
9264                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9265             }
9266             
9267         }
9268         
9269         this.fireEvent('valid', this);
9270     },
9271     
9272      /**
9273      * Mark this field as valid
9274      */
9275     markValid : function()
9276     {
9277         if(!this.el  || this.preventMark){ // not rendered
9278             return;
9279         }
9280         
9281         this.el.removeClass([this.invalidClass, this.validClass]);
9282         
9283         var feedback = this.el.select('.form-control-feedback', true).first();
9284             
9285         if(feedback){
9286             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9287         }
9288
9289         if(this.disabled || this.allowBlank){
9290             return;
9291         }
9292         
9293         var label = this.el.select('label', true).first();
9294         var icon = this.el.select('i.fa-star', true).first();
9295         
9296         if(label && icon){
9297             icon.remove();
9298         }
9299         
9300         this.el.addClass(this.validClass);
9301         
9302         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9303             
9304             var feedback = this.el.select('.form-control-feedback', true).first();
9305             
9306             if(feedback){
9307                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9308                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9309             }
9310             
9311         }
9312         
9313         this.fireEvent('valid', this);
9314     },
9315     
9316      /**
9317      * Mark this field as invalid
9318      * @param {String} msg The validation message
9319      */
9320     markInvalid : function(msg)
9321     {
9322         if(!this.el  || this.preventMark){ // not rendered
9323             return;
9324         }
9325         
9326         this.el.removeClass([this.invalidClass, this.validClass]);
9327         
9328         var feedback = this.el.select('.form-control-feedback', true).first();
9329             
9330         if(feedback){
9331             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9332         }
9333
9334         if(this.disabled || this.allowBlank){
9335             return;
9336         }
9337         
9338         var label = this.el.select('label', true).first();
9339         var icon = this.el.select('i.fa-star', true).first();
9340         
9341         if(!this.getValue().length && label && !icon){
9342             this.el.createChild({
9343                 tag : 'i',
9344                 cls : 'text-danger fa fa-lg fa-star',
9345                 tooltip : 'This field is required',
9346                 style : 'margin-right:5px;'
9347             }, label, true);
9348         }
9349
9350         this.el.addClass(this.invalidClass);
9351         
9352         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9353             
9354             var feedback = this.el.select('.form-control-feedback', true).first();
9355             
9356             if(feedback){
9357                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9358                 
9359                 if(this.getValue().length || this.forceFeedback){
9360                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9361                 }
9362                 
9363             }
9364             
9365         }
9366         
9367         this.fireEvent('invalid', this, msg);
9368     }
9369 });
9370
9371  
9372 /*
9373  * - LGPL
9374  *
9375  * trigger field - base class for combo..
9376  * 
9377  */
9378  
9379 /**
9380  * @class Roo.bootstrap.TriggerField
9381  * @extends Roo.bootstrap.Input
9382  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9383  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9384  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9385  * for which you can provide a custom implementation.  For example:
9386  * <pre><code>
9387 var trigger = new Roo.bootstrap.TriggerField();
9388 trigger.onTriggerClick = myTriggerFn;
9389 trigger.applyTo('my-field');
9390 </code></pre>
9391  *
9392  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9393  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9394  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9395  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9396  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9397
9398  * @constructor
9399  * Create a new TriggerField.
9400  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9401  * to the base TextField)
9402  */
9403 Roo.bootstrap.TriggerField = function(config){
9404     this.mimicing = false;
9405     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9406 };
9407
9408 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9409     /**
9410      * @cfg {String} triggerClass A CSS class to apply to the trigger
9411      */
9412      /**
9413      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9414      */
9415     hideTrigger:false,
9416
9417     /**
9418      * @cfg {Boolean} removable (true|false) special filter default false
9419      */
9420     removable : false,
9421     
9422     /** @cfg {Boolean} grow @hide */
9423     /** @cfg {Number} growMin @hide */
9424     /** @cfg {Number} growMax @hide */
9425
9426     /**
9427      * @hide 
9428      * @method
9429      */
9430     autoSize: Roo.emptyFn,
9431     // private
9432     monitorTab : true,
9433     // private
9434     deferHeight : true,
9435
9436     
9437     actionMode : 'wrap',
9438     
9439     caret : false,
9440     
9441     
9442     getAutoCreate : function(){
9443        
9444         var align = this.labelAlign || this.parentLabelAlign();
9445         
9446         var id = Roo.id();
9447         
9448         var cfg = {
9449             cls: 'form-group' //input-group
9450         };
9451         
9452         
9453         var input =  {
9454             tag: 'input',
9455             id : id,
9456             type : this.inputType,
9457             cls : 'form-control',
9458             autocomplete: 'new-password',
9459             placeholder : this.placeholder || '' 
9460             
9461         };
9462         if (this.name) {
9463             input.name = this.name;
9464         }
9465         if (this.size) {
9466             input.cls += ' input-' + this.size;
9467         }
9468         
9469         if (this.disabled) {
9470             input.disabled=true;
9471         }
9472         
9473         var inputblock = input;
9474         
9475         if(this.hasFeedback && !this.allowBlank){
9476             
9477             var feedback = {
9478                 tag: 'span',
9479                 cls: 'glyphicon form-control-feedback'
9480             };
9481             
9482             if(this.removable && !this.editable && !this.tickable){
9483                 inputblock = {
9484                     cls : 'has-feedback',
9485                     cn :  [
9486                         inputblock,
9487                         {
9488                             tag: 'button',
9489                             html : 'x',
9490                             cls : 'roo-combo-removable-btn close'
9491                         },
9492                         feedback
9493                     ] 
9494                 };
9495             } else {
9496                 inputblock = {
9497                     cls : 'has-feedback',
9498                     cn :  [
9499                         inputblock,
9500                         feedback
9501                     ] 
9502                 };
9503             }
9504
9505         } else {
9506             if(this.removable && !this.editable && !this.tickable){
9507                 inputblock = {
9508                     cls : 'roo-removable',
9509                     cn :  [
9510                         inputblock,
9511                         {
9512                             tag: 'button',
9513                             html : 'x',
9514                             cls : 'roo-combo-removable-btn close'
9515                         }
9516                     ] 
9517                 };
9518             }
9519         }
9520         
9521         if (this.before || this.after) {
9522             
9523             inputblock = {
9524                 cls : 'input-group',
9525                 cn :  [] 
9526             };
9527             if (this.before) {
9528                 inputblock.cn.push({
9529                     tag :'span',
9530                     cls : 'input-group-addon',
9531                     html : this.before
9532                 });
9533             }
9534             
9535             inputblock.cn.push(input);
9536             
9537             if(this.hasFeedback && !this.allowBlank){
9538                 inputblock.cls += ' has-feedback';
9539                 inputblock.cn.push(feedback);
9540             }
9541             
9542             if (this.after) {
9543                 inputblock.cn.push({
9544                     tag :'span',
9545                     cls : 'input-group-addon',
9546                     html : this.after
9547                 });
9548             }
9549             
9550         };
9551         
9552         var box = {
9553             tag: 'div',
9554             cn: [
9555                 {
9556                     tag: 'input',
9557                     type : 'hidden',
9558                     cls: 'form-hidden-field'
9559                 },
9560                 inputblock
9561             ]
9562             
9563         };
9564         
9565         if(this.multiple){
9566             box = {
9567                 tag: 'div',
9568                 cn: [
9569                     {
9570                         tag: 'input',
9571                         type : 'hidden',
9572                         cls: 'form-hidden-field'
9573                     },
9574                     {
9575                         tag: 'ul',
9576                         cls: 'roo-select2-choices',
9577                         cn:[
9578                             {
9579                                 tag: 'li',
9580                                 cls: 'roo-select2-search-field',
9581                                 cn: [
9582
9583                                     inputblock
9584                                 ]
9585                             }
9586                         ]
9587                     }
9588                 ]
9589             }
9590         };
9591         
9592         var combobox = {
9593             cls: 'roo-select2-container input-group',
9594             cn: [
9595                 box
9596 //                {
9597 //                    tag: 'ul',
9598 //                    cls: 'typeahead typeahead-long dropdown-menu',
9599 //                    style: 'display:none'
9600 //                }
9601             ]
9602         };
9603         
9604         if(!this.multiple && this.showToggleBtn){
9605             
9606             var caret = {
9607                         tag: 'span',
9608                         cls: 'caret'
9609              };
9610             if (this.caret != false) {
9611                 caret = {
9612                      tag: 'i',
9613                      cls: 'fa fa-' + this.caret
9614                 };
9615                 
9616             }
9617             
9618             combobox.cn.push({
9619                 tag :'span',
9620                 cls : 'input-group-addon btn dropdown-toggle',
9621                 cn : [
9622                     caret,
9623                     {
9624                         tag: 'span',
9625                         cls: 'combobox-clear',
9626                         cn  : [
9627                             {
9628                                 tag : 'i',
9629                                 cls: 'icon-remove'
9630                             }
9631                         ]
9632                     }
9633                 ]
9634
9635             })
9636         }
9637         
9638         if(this.multiple){
9639             combobox.cls += ' roo-select2-container-multi';
9640         }
9641         
9642         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9643             
9644 //                Roo.log("left and has label");
9645             cfg.cn = [
9646                 {
9647                     tag : 'i',
9648                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9649                     tooltip : 'This field is required'
9650                 },
9651                 {
9652                     tag: 'label',
9653                     'for' :  id,
9654                     cls : 'control-label col-sm-' + this.labelWidth,
9655                     html : this.fieldLabel
9656
9657                 },
9658                 {
9659                     cls : "col-sm-" + (12 - this.labelWidth), 
9660                     cn: [
9661                         combobox
9662                     ]
9663                 }
9664
9665             ];
9666             
9667             if(this.indicatorpos == 'right'){
9668                 cfg.cn = [
9669                     {
9670                         tag: 'label',
9671                         'for' :  id,
9672                         cls : 'control-label col-sm-' + this.labelWidth,
9673                         html : this.fieldLabel
9674
9675                     },
9676                     {
9677                         tag : 'i',
9678                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9679                         tooltip : 'This field is required'
9680                     },
9681                     {
9682                         cls : "col-sm-" + (12 - this.labelWidth), 
9683                         cn: [
9684                             combobox
9685                         ]
9686                     }
9687
9688                 ];
9689             }
9690             
9691         } else if ( this.fieldLabel.length) {
9692 //                Roo.log(" label");
9693             cfg.cn = [
9694                 {
9695                    tag : 'i',
9696                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9697                    tooltip : 'This field is required'
9698                },
9699                {
9700                    tag: 'label',
9701                    //cls : 'input-group-addon',
9702                    html : this.fieldLabel
9703
9704                },
9705
9706                combobox
9707
9708             ];
9709             
9710             if(this.indicatorpos == 'right'){
9711                 
9712                 cfg.cn = [
9713                     {
9714                        tag: 'label',
9715                        //cls : 'input-group-addon',
9716                        html : this.fieldLabel
9717
9718                     },
9719                     {
9720                        tag : 'i',
9721                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9722                        tooltip : 'This field is required'
9723                     },
9724                     
9725                     combobox
9726
9727                 ];
9728
9729             }
9730
9731         } else {
9732             
9733 //                Roo.log(" no label && no align");
9734                 cfg = combobox
9735                      
9736                 
9737         }
9738          
9739         var settings=this;
9740         ['xs','sm','md','lg'].map(function(size){
9741             if (settings[size]) {
9742                 cfg.cls += ' col-' + size + '-' + settings[size];
9743             }
9744         });
9745         
9746         return cfg;
9747         
9748     },
9749     
9750     
9751     
9752     // private
9753     onResize : function(w, h){
9754 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9755 //        if(typeof w == 'number'){
9756 //            var x = w - this.trigger.getWidth();
9757 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9758 //            this.trigger.setStyle('left', x+'px');
9759 //        }
9760     },
9761
9762     // private
9763     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9764
9765     // private
9766     getResizeEl : function(){
9767         return this.inputEl();
9768     },
9769
9770     // private
9771     getPositionEl : function(){
9772         return this.inputEl();
9773     },
9774
9775     // private
9776     alignErrorIcon : function(){
9777         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9778     },
9779
9780     // private
9781     initEvents : function(){
9782         
9783         this.createList();
9784         
9785         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9786         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9787         if(!this.multiple && this.showToggleBtn){
9788             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9789             if(this.hideTrigger){
9790                 this.trigger.setDisplayed(false);
9791             }
9792             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9793         }
9794         
9795         if(this.multiple){
9796             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9797         }
9798         
9799         if(this.removable && !this.editable && !this.tickable){
9800             var close = this.closeTriggerEl();
9801             
9802             if(close){
9803                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9804                 close.on('click', this.removeBtnClick, this, close);
9805             }
9806         }
9807         
9808         //this.trigger.addClassOnOver('x-form-trigger-over');
9809         //this.trigger.addClassOnClick('x-form-trigger-click');
9810         
9811         //if(!this.width){
9812         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9813         //}
9814     },
9815     
9816     closeTriggerEl : function()
9817     {
9818         var close = this.el.select('.roo-combo-removable-btn', true).first();
9819         return close ? close : false;
9820     },
9821     
9822     removeBtnClick : function(e, h, el)
9823     {
9824         e.preventDefault();
9825         
9826         if(this.fireEvent("remove", this) !== false){
9827             this.reset();
9828             this.fireEvent("afterremove", this)
9829         }
9830     },
9831     
9832     createList : function()
9833     {
9834         this.list = Roo.get(document.body).createChild({
9835             tag: 'ul',
9836             cls: 'typeahead typeahead-long dropdown-menu',
9837             style: 'display:none'
9838         });
9839         
9840         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9841         
9842     },
9843
9844     // private
9845     initTrigger : function(){
9846        
9847     },
9848
9849     // private
9850     onDestroy : function(){
9851         if(this.trigger){
9852             this.trigger.removeAllListeners();
9853           //  this.trigger.remove();
9854         }
9855         //if(this.wrap){
9856         //    this.wrap.remove();
9857         //}
9858         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9859     },
9860
9861     // private
9862     onFocus : function(){
9863         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9864         /*
9865         if(!this.mimicing){
9866             this.wrap.addClass('x-trigger-wrap-focus');
9867             this.mimicing = true;
9868             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9869             if(this.monitorTab){
9870                 this.el.on("keydown", this.checkTab, this);
9871             }
9872         }
9873         */
9874     },
9875
9876     // private
9877     checkTab : function(e){
9878         if(e.getKey() == e.TAB){
9879             this.triggerBlur();
9880         }
9881     },
9882
9883     // private
9884     onBlur : function(){
9885         // do nothing
9886     },
9887
9888     // private
9889     mimicBlur : function(e, t){
9890         /*
9891         if(!this.wrap.contains(t) && this.validateBlur()){
9892             this.triggerBlur();
9893         }
9894         */
9895     },
9896
9897     // private
9898     triggerBlur : function(){
9899         this.mimicing = false;
9900         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9901         if(this.monitorTab){
9902             this.el.un("keydown", this.checkTab, this);
9903         }
9904         //this.wrap.removeClass('x-trigger-wrap-focus');
9905         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9906     },
9907
9908     // private
9909     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9910     validateBlur : function(e, t){
9911         return true;
9912     },
9913
9914     // private
9915     onDisable : function(){
9916         this.inputEl().dom.disabled = true;
9917         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9918         //if(this.wrap){
9919         //    this.wrap.addClass('x-item-disabled');
9920         //}
9921     },
9922
9923     // private
9924     onEnable : function(){
9925         this.inputEl().dom.disabled = false;
9926         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9927         //if(this.wrap){
9928         //    this.el.removeClass('x-item-disabled');
9929         //}
9930     },
9931
9932     // private
9933     onShow : function(){
9934         var ae = this.getActionEl();
9935         
9936         if(ae){
9937             ae.dom.style.display = '';
9938             ae.dom.style.visibility = 'visible';
9939         }
9940     },
9941
9942     // private
9943     
9944     onHide : function(){
9945         var ae = this.getActionEl();
9946         ae.dom.style.display = 'none';
9947     },
9948
9949     /**
9950      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9951      * by an implementing function.
9952      * @method
9953      * @param {EventObject} e
9954      */
9955     onTriggerClick : Roo.emptyFn
9956 });
9957  /*
9958  * Based on:
9959  * Ext JS Library 1.1.1
9960  * Copyright(c) 2006-2007, Ext JS, LLC.
9961  *
9962  * Originally Released Under LGPL - original licence link has changed is not relivant.
9963  *
9964  * Fork - LGPL
9965  * <script type="text/javascript">
9966  */
9967
9968
9969 /**
9970  * @class Roo.data.SortTypes
9971  * @singleton
9972  * Defines the default sorting (casting?) comparison functions used when sorting data.
9973  */
9974 Roo.data.SortTypes = {
9975     /**
9976      * Default sort that does nothing
9977      * @param {Mixed} s The value being converted
9978      * @return {Mixed} The comparison value
9979      */
9980     none : function(s){
9981         return s;
9982     },
9983     
9984     /**
9985      * The regular expression used to strip tags
9986      * @type {RegExp}
9987      * @property
9988      */
9989     stripTagsRE : /<\/?[^>]+>/gi,
9990     
9991     /**
9992      * Strips all HTML tags to sort on text only
9993      * @param {Mixed} s The value being converted
9994      * @return {String} The comparison value
9995      */
9996     asText : function(s){
9997         return String(s).replace(this.stripTagsRE, "");
9998     },
9999     
10000     /**
10001      * Strips all HTML tags to sort on text only - Case insensitive
10002      * @param {Mixed} s The value being converted
10003      * @return {String} The comparison value
10004      */
10005     asUCText : function(s){
10006         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10007     },
10008     
10009     /**
10010      * Case insensitive string
10011      * @param {Mixed} s The value being converted
10012      * @return {String} The comparison value
10013      */
10014     asUCString : function(s) {
10015         return String(s).toUpperCase();
10016     },
10017     
10018     /**
10019      * Date sorting
10020      * @param {Mixed} s The value being converted
10021      * @return {Number} The comparison value
10022      */
10023     asDate : function(s) {
10024         if(!s){
10025             return 0;
10026         }
10027         if(s instanceof Date){
10028             return s.getTime();
10029         }
10030         return Date.parse(String(s));
10031     },
10032     
10033     /**
10034      * Float sorting
10035      * @param {Mixed} s The value being converted
10036      * @return {Float} The comparison value
10037      */
10038     asFloat : function(s) {
10039         var val = parseFloat(String(s).replace(/,/g, ""));
10040         if(isNaN(val)) {
10041             val = 0;
10042         }
10043         return val;
10044     },
10045     
10046     /**
10047      * Integer sorting
10048      * @param {Mixed} s The value being converted
10049      * @return {Number} The comparison value
10050      */
10051     asInt : function(s) {
10052         var val = parseInt(String(s).replace(/,/g, ""));
10053         if(isNaN(val)) {
10054             val = 0;
10055         }
10056         return val;
10057     }
10058 };/*
10059  * Based on:
10060  * Ext JS Library 1.1.1
10061  * Copyright(c) 2006-2007, Ext JS, LLC.
10062  *
10063  * Originally Released Under LGPL - original licence link has changed is not relivant.
10064  *
10065  * Fork - LGPL
10066  * <script type="text/javascript">
10067  */
10068
10069 /**
10070 * @class Roo.data.Record
10071  * Instances of this class encapsulate both record <em>definition</em> information, and record
10072  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10073  * to access Records cached in an {@link Roo.data.Store} object.<br>
10074  * <p>
10075  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10076  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10077  * objects.<br>
10078  * <p>
10079  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10080  * @constructor
10081  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10082  * {@link #create}. The parameters are the same.
10083  * @param {Array} data An associative Array of data values keyed by the field name.
10084  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10085  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10086  * not specified an integer id is generated.
10087  */
10088 Roo.data.Record = function(data, id){
10089     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10090     this.data = data;
10091 };
10092
10093 /**
10094  * Generate a constructor for a specific record layout.
10095  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10096  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10097  * Each field definition object may contain the following properties: <ul>
10098  * <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,
10099  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10100  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10101  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10102  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10103  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10104  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10105  * this may be omitted.</p></li>
10106  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10107  * <ul><li>auto (Default, implies no conversion)</li>
10108  * <li>string</li>
10109  * <li>int</li>
10110  * <li>float</li>
10111  * <li>boolean</li>
10112  * <li>date</li></ul></p></li>
10113  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10114  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10115  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10116  * by the Reader into an object that will be stored in the Record. It is passed the
10117  * following parameters:<ul>
10118  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10119  * </ul></p></li>
10120  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10121  * </ul>
10122  * <br>usage:<br><pre><code>
10123 var TopicRecord = Roo.data.Record.create(
10124     {name: 'title', mapping: 'topic_title'},
10125     {name: 'author', mapping: 'username'},
10126     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10127     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10128     {name: 'lastPoster', mapping: 'user2'},
10129     {name: 'excerpt', mapping: 'post_text'}
10130 );
10131
10132 var myNewRecord = new TopicRecord({
10133     title: 'Do my job please',
10134     author: 'noobie',
10135     totalPosts: 1,
10136     lastPost: new Date(),
10137     lastPoster: 'Animal',
10138     excerpt: 'No way dude!'
10139 });
10140 myStore.add(myNewRecord);
10141 </code></pre>
10142  * @method create
10143  * @static
10144  */
10145 Roo.data.Record.create = function(o){
10146     var f = function(){
10147         f.superclass.constructor.apply(this, arguments);
10148     };
10149     Roo.extend(f, Roo.data.Record);
10150     var p = f.prototype;
10151     p.fields = new Roo.util.MixedCollection(false, function(field){
10152         return field.name;
10153     });
10154     for(var i = 0, len = o.length; i < len; i++){
10155         p.fields.add(new Roo.data.Field(o[i]));
10156     }
10157     f.getField = function(name){
10158         return p.fields.get(name);  
10159     };
10160     return f;
10161 };
10162
10163 Roo.data.Record.AUTO_ID = 1000;
10164 Roo.data.Record.EDIT = 'edit';
10165 Roo.data.Record.REJECT = 'reject';
10166 Roo.data.Record.COMMIT = 'commit';
10167
10168 Roo.data.Record.prototype = {
10169     /**
10170      * Readonly flag - true if this record has been modified.
10171      * @type Boolean
10172      */
10173     dirty : false,
10174     editing : false,
10175     error: null,
10176     modified: null,
10177
10178     // private
10179     join : function(store){
10180         this.store = store;
10181     },
10182
10183     /**
10184      * Set the named field to the specified value.
10185      * @param {String} name The name of the field to set.
10186      * @param {Object} value The value to set the field to.
10187      */
10188     set : function(name, value){
10189         if(this.data[name] == value){
10190             return;
10191         }
10192         this.dirty = true;
10193         if(!this.modified){
10194             this.modified = {};
10195         }
10196         if(typeof this.modified[name] == 'undefined'){
10197             this.modified[name] = this.data[name];
10198         }
10199         this.data[name] = value;
10200         if(!this.editing && this.store){
10201             this.store.afterEdit(this);
10202         }       
10203     },
10204
10205     /**
10206      * Get the value of the named field.
10207      * @param {String} name The name of the field to get the value of.
10208      * @return {Object} The value of the field.
10209      */
10210     get : function(name){
10211         return this.data[name]; 
10212     },
10213
10214     // private
10215     beginEdit : function(){
10216         this.editing = true;
10217         this.modified = {}; 
10218     },
10219
10220     // private
10221     cancelEdit : function(){
10222         this.editing = false;
10223         delete this.modified;
10224     },
10225
10226     // private
10227     endEdit : function(){
10228         this.editing = false;
10229         if(this.dirty && this.store){
10230             this.store.afterEdit(this);
10231         }
10232     },
10233
10234     /**
10235      * Usually called by the {@link Roo.data.Store} which owns the Record.
10236      * Rejects all changes made to the Record since either creation, or the last commit operation.
10237      * Modified fields are reverted to their original values.
10238      * <p>
10239      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10240      * of reject operations.
10241      */
10242     reject : function(){
10243         var m = this.modified;
10244         for(var n in m){
10245             if(typeof m[n] != "function"){
10246                 this.data[n] = m[n];
10247             }
10248         }
10249         this.dirty = false;
10250         delete this.modified;
10251         this.editing = false;
10252         if(this.store){
10253             this.store.afterReject(this);
10254         }
10255     },
10256
10257     /**
10258      * Usually called by the {@link Roo.data.Store} which owns the Record.
10259      * Commits all changes made to the Record since either creation, or the last commit operation.
10260      * <p>
10261      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10262      * of commit operations.
10263      */
10264     commit : function(){
10265         this.dirty = false;
10266         delete this.modified;
10267         this.editing = false;
10268         if(this.store){
10269             this.store.afterCommit(this);
10270         }
10271     },
10272
10273     // private
10274     hasError : function(){
10275         return this.error != null;
10276     },
10277
10278     // private
10279     clearError : function(){
10280         this.error = null;
10281     },
10282
10283     /**
10284      * Creates a copy of this record.
10285      * @param {String} id (optional) A new record id if you don't want to use this record's id
10286      * @return {Record}
10287      */
10288     copy : function(newId) {
10289         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10290     }
10291 };/*
10292  * Based on:
10293  * Ext JS Library 1.1.1
10294  * Copyright(c) 2006-2007, Ext JS, LLC.
10295  *
10296  * Originally Released Under LGPL - original licence link has changed is not relivant.
10297  *
10298  * Fork - LGPL
10299  * <script type="text/javascript">
10300  */
10301
10302
10303
10304 /**
10305  * @class Roo.data.Store
10306  * @extends Roo.util.Observable
10307  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10308  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10309  * <p>
10310  * 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
10311  * has no knowledge of the format of the data returned by the Proxy.<br>
10312  * <p>
10313  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10314  * instances from the data object. These records are cached and made available through accessor functions.
10315  * @constructor
10316  * Creates a new Store.
10317  * @param {Object} config A config object containing the objects needed for the Store to access data,
10318  * and read the data into Records.
10319  */
10320 Roo.data.Store = function(config){
10321     this.data = new Roo.util.MixedCollection(false);
10322     this.data.getKey = function(o){
10323         return o.id;
10324     };
10325     this.baseParams = {};
10326     // private
10327     this.paramNames = {
10328         "start" : "start",
10329         "limit" : "limit",
10330         "sort" : "sort",
10331         "dir" : "dir",
10332         "multisort" : "_multisort"
10333     };
10334
10335     if(config && config.data){
10336         this.inlineData = config.data;
10337         delete config.data;
10338     }
10339
10340     Roo.apply(this, config);
10341     
10342     if(this.reader){ // reader passed
10343         this.reader = Roo.factory(this.reader, Roo.data);
10344         this.reader.xmodule = this.xmodule || false;
10345         if(!this.recordType){
10346             this.recordType = this.reader.recordType;
10347         }
10348         if(this.reader.onMetaChange){
10349             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10350         }
10351     }
10352
10353     if(this.recordType){
10354         this.fields = this.recordType.prototype.fields;
10355     }
10356     this.modified = [];
10357
10358     this.addEvents({
10359         /**
10360          * @event datachanged
10361          * Fires when the data cache has changed, and a widget which is using this Store
10362          * as a Record cache should refresh its view.
10363          * @param {Store} this
10364          */
10365         datachanged : true,
10366         /**
10367          * @event metachange
10368          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10369          * @param {Store} this
10370          * @param {Object} meta The JSON metadata
10371          */
10372         metachange : true,
10373         /**
10374          * @event add
10375          * Fires when Records have been added to the Store
10376          * @param {Store} this
10377          * @param {Roo.data.Record[]} records The array of Records added
10378          * @param {Number} index The index at which the record(s) were added
10379          */
10380         add : true,
10381         /**
10382          * @event remove
10383          * Fires when a Record has been removed from the Store
10384          * @param {Store} this
10385          * @param {Roo.data.Record} record The Record that was removed
10386          * @param {Number} index The index at which the record was removed
10387          */
10388         remove : true,
10389         /**
10390          * @event update
10391          * Fires when a Record has been updated
10392          * @param {Store} this
10393          * @param {Roo.data.Record} record The Record that was updated
10394          * @param {String} operation The update operation being performed.  Value may be one of:
10395          * <pre><code>
10396  Roo.data.Record.EDIT
10397  Roo.data.Record.REJECT
10398  Roo.data.Record.COMMIT
10399          * </code></pre>
10400          */
10401         update : true,
10402         /**
10403          * @event clear
10404          * Fires when the data cache has been cleared.
10405          * @param {Store} this
10406          */
10407         clear : true,
10408         /**
10409          * @event beforeload
10410          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10411          * the load action will be canceled.
10412          * @param {Store} this
10413          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10414          */
10415         beforeload : true,
10416         /**
10417          * @event beforeloadadd
10418          * Fires after a new set of Records has been loaded.
10419          * @param {Store} this
10420          * @param {Roo.data.Record[]} records The Records that were loaded
10421          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10422          */
10423         beforeloadadd : true,
10424         /**
10425          * @event load
10426          * Fires after a new set of Records has been loaded, before they are added to the store.
10427          * @param {Store} this
10428          * @param {Roo.data.Record[]} records The Records that were loaded
10429          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10430          * @params {Object} return from reader
10431          */
10432         load : true,
10433         /**
10434          * @event loadexception
10435          * Fires if an exception occurs in the Proxy during loading.
10436          * Called with the signature of the Proxy's "loadexception" event.
10437          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10438          * 
10439          * @param {Proxy} 
10440          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10441          * @param {Object} load options 
10442          * @param {Object} jsonData from your request (normally this contains the Exception)
10443          */
10444         loadexception : true
10445     });
10446     
10447     if(this.proxy){
10448         this.proxy = Roo.factory(this.proxy, Roo.data);
10449         this.proxy.xmodule = this.xmodule || false;
10450         this.relayEvents(this.proxy,  ["loadexception"]);
10451     }
10452     this.sortToggle = {};
10453     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10454
10455     Roo.data.Store.superclass.constructor.call(this);
10456
10457     if(this.inlineData){
10458         this.loadData(this.inlineData);
10459         delete this.inlineData;
10460     }
10461 };
10462
10463 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10464      /**
10465     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10466     * without a remote query - used by combo/forms at present.
10467     */
10468     
10469     /**
10470     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10471     */
10472     /**
10473     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10474     */
10475     /**
10476     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10477     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10478     */
10479     /**
10480     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10481     * on any HTTP request
10482     */
10483     /**
10484     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10485     */
10486     /**
10487     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10488     */
10489     multiSort: false,
10490     /**
10491     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10492     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10493     */
10494     remoteSort : false,
10495
10496     /**
10497     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10498      * loaded or when a record is removed. (defaults to false).
10499     */
10500     pruneModifiedRecords : false,
10501
10502     // private
10503     lastOptions : null,
10504
10505     /**
10506      * Add Records to the Store and fires the add event.
10507      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10508      */
10509     add : function(records){
10510         records = [].concat(records);
10511         for(var i = 0, len = records.length; i < len; i++){
10512             records[i].join(this);
10513         }
10514         var index = this.data.length;
10515         this.data.addAll(records);
10516         this.fireEvent("add", this, records, index);
10517     },
10518
10519     /**
10520      * Remove a Record from the Store and fires the remove event.
10521      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10522      */
10523     remove : function(record){
10524         var index = this.data.indexOf(record);
10525         this.data.removeAt(index);
10526         if(this.pruneModifiedRecords){
10527             this.modified.remove(record);
10528         }
10529         this.fireEvent("remove", this, record, index);
10530     },
10531
10532     /**
10533      * Remove all Records from the Store and fires the clear event.
10534      */
10535     removeAll : function(){
10536         this.data.clear();
10537         if(this.pruneModifiedRecords){
10538             this.modified = [];
10539         }
10540         this.fireEvent("clear", this);
10541     },
10542
10543     /**
10544      * Inserts Records to the Store at the given index and fires the add event.
10545      * @param {Number} index The start index at which to insert the passed Records.
10546      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10547      */
10548     insert : function(index, records){
10549         records = [].concat(records);
10550         for(var i = 0, len = records.length; i < len; i++){
10551             this.data.insert(index, records[i]);
10552             records[i].join(this);
10553         }
10554         this.fireEvent("add", this, records, index);
10555     },
10556
10557     /**
10558      * Get the index within the cache of the passed Record.
10559      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10560      * @return {Number} The index of the passed Record. Returns -1 if not found.
10561      */
10562     indexOf : function(record){
10563         return this.data.indexOf(record);
10564     },
10565
10566     /**
10567      * Get the index within the cache of the Record with the passed id.
10568      * @param {String} id The id of the Record to find.
10569      * @return {Number} The index of the Record. Returns -1 if not found.
10570      */
10571     indexOfId : function(id){
10572         return this.data.indexOfKey(id);
10573     },
10574
10575     /**
10576      * Get the Record with the specified id.
10577      * @param {String} id The id of the Record to find.
10578      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10579      */
10580     getById : function(id){
10581         return this.data.key(id);
10582     },
10583
10584     /**
10585      * Get the Record at the specified index.
10586      * @param {Number} index The index of the Record to find.
10587      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10588      */
10589     getAt : function(index){
10590         return this.data.itemAt(index);
10591     },
10592
10593     /**
10594      * Returns a range of Records between specified indices.
10595      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10596      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10597      * @return {Roo.data.Record[]} An array of Records
10598      */
10599     getRange : function(start, end){
10600         return this.data.getRange(start, end);
10601     },
10602
10603     // private
10604     storeOptions : function(o){
10605         o = Roo.apply({}, o);
10606         delete o.callback;
10607         delete o.scope;
10608         this.lastOptions = o;
10609     },
10610
10611     /**
10612      * Loads the Record cache from the configured Proxy using the configured Reader.
10613      * <p>
10614      * If using remote paging, then the first load call must specify the <em>start</em>
10615      * and <em>limit</em> properties in the options.params property to establish the initial
10616      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10617      * <p>
10618      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10619      * and this call will return before the new data has been loaded. Perform any post-processing
10620      * in a callback function, or in a "load" event handler.</strong>
10621      * <p>
10622      * @param {Object} options An object containing properties which control loading options:<ul>
10623      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10624      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10625      * passed the following arguments:<ul>
10626      * <li>r : Roo.data.Record[]</li>
10627      * <li>options: Options object from the load call</li>
10628      * <li>success: Boolean success indicator</li></ul></li>
10629      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10630      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10631      * </ul>
10632      */
10633     load : function(options){
10634         options = options || {};
10635         if(this.fireEvent("beforeload", this, options) !== false){
10636             this.storeOptions(options);
10637             var p = Roo.apply(options.params || {}, this.baseParams);
10638             // if meta was not loaded from remote source.. try requesting it.
10639             if (!this.reader.metaFromRemote) {
10640                 p._requestMeta = 1;
10641             }
10642             if(this.sortInfo && this.remoteSort){
10643                 var pn = this.paramNames;
10644                 p[pn["sort"]] = this.sortInfo.field;
10645                 p[pn["dir"]] = this.sortInfo.direction;
10646             }
10647             if (this.multiSort) {
10648                 var pn = this.paramNames;
10649                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10650             }
10651             
10652             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10653         }
10654     },
10655
10656     /**
10657      * Reloads the Record cache from the configured Proxy using the configured Reader and
10658      * the options from the last load operation performed.
10659      * @param {Object} options (optional) An object containing properties which may override the options
10660      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10661      * the most recently used options are reused).
10662      */
10663     reload : function(options){
10664         this.load(Roo.applyIf(options||{}, this.lastOptions));
10665     },
10666
10667     // private
10668     // Called as a callback by the Reader during a load operation.
10669     loadRecords : function(o, options, success){
10670         if(!o || success === false){
10671             if(success !== false){
10672                 this.fireEvent("load", this, [], options, o);
10673             }
10674             if(options.callback){
10675                 options.callback.call(options.scope || this, [], options, false);
10676             }
10677             return;
10678         }
10679         // if data returned failure - throw an exception.
10680         if (o.success === false) {
10681             // show a message if no listener is registered.
10682             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10683                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10684             }
10685             // loadmask wil be hooked into this..
10686             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10687             return;
10688         }
10689         var r = o.records, t = o.totalRecords || r.length;
10690         
10691         this.fireEvent("beforeloadadd", this, r, options, o);
10692         
10693         if(!options || options.add !== true){
10694             if(this.pruneModifiedRecords){
10695                 this.modified = [];
10696             }
10697             for(var i = 0, len = r.length; i < len; i++){
10698                 r[i].join(this);
10699             }
10700             if(this.snapshot){
10701                 this.data = this.snapshot;
10702                 delete this.snapshot;
10703             }
10704             this.data.clear();
10705             this.data.addAll(r);
10706             this.totalLength = t;
10707             this.applySort();
10708             this.fireEvent("datachanged", this);
10709         }else{
10710             this.totalLength = Math.max(t, this.data.length+r.length);
10711             this.add(r);
10712         }
10713         this.fireEvent("load", this, r, options, o);
10714         if(options.callback){
10715             options.callback.call(options.scope || this, r, options, true);
10716         }
10717     },
10718
10719
10720     /**
10721      * Loads data from a passed data block. A Reader which understands the format of the data
10722      * must have been configured in the constructor.
10723      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10724      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10725      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10726      */
10727     loadData : function(o, append){
10728         var r = this.reader.readRecords(o);
10729         this.loadRecords(r, {add: append}, true);
10730     },
10731
10732     /**
10733      * Gets the number of cached records.
10734      * <p>
10735      * <em>If using paging, this may not be the total size of the dataset. If the data object
10736      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10737      * the data set size</em>
10738      */
10739     getCount : function(){
10740         return this.data.length || 0;
10741     },
10742
10743     /**
10744      * Gets the total number of records in the dataset as returned by the server.
10745      * <p>
10746      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10747      * the dataset size</em>
10748      */
10749     getTotalCount : function(){
10750         return this.totalLength || 0;
10751     },
10752
10753     /**
10754      * Returns the sort state of the Store as an object with two properties:
10755      * <pre><code>
10756  field {String} The name of the field by which the Records are sorted
10757  direction {String} The sort order, "ASC" or "DESC"
10758      * </code></pre>
10759      */
10760     getSortState : function(){
10761         return this.sortInfo;
10762     },
10763
10764     // private
10765     applySort : function(){
10766         if(this.sortInfo && !this.remoteSort){
10767             var s = this.sortInfo, f = s.field;
10768             var st = this.fields.get(f).sortType;
10769             var fn = function(r1, r2){
10770                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10771                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10772             };
10773             this.data.sort(s.direction, fn);
10774             if(this.snapshot && this.snapshot != this.data){
10775                 this.snapshot.sort(s.direction, fn);
10776             }
10777         }
10778     },
10779
10780     /**
10781      * Sets the default sort column and order to be used by the next load operation.
10782      * @param {String} fieldName The name of the field to sort by.
10783      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10784      */
10785     setDefaultSort : function(field, dir){
10786         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10787     },
10788
10789     /**
10790      * Sort the Records.
10791      * If remote sorting is used, the sort is performed on the server, and the cache is
10792      * reloaded. If local sorting is used, the cache is sorted internally.
10793      * @param {String} fieldName The name of the field to sort by.
10794      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10795      */
10796     sort : function(fieldName, dir){
10797         var f = this.fields.get(fieldName);
10798         if(!dir){
10799             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10800             
10801             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10802                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10803             }else{
10804                 dir = f.sortDir;
10805             }
10806         }
10807         this.sortToggle[f.name] = dir;
10808         this.sortInfo = {field: f.name, direction: dir};
10809         if(!this.remoteSort){
10810             this.applySort();
10811             this.fireEvent("datachanged", this);
10812         }else{
10813             this.load(this.lastOptions);
10814         }
10815     },
10816
10817     /**
10818      * Calls the specified function for each of the Records in the cache.
10819      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10820      * Returning <em>false</em> aborts and exits the iteration.
10821      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10822      */
10823     each : function(fn, scope){
10824         this.data.each(fn, scope);
10825     },
10826
10827     /**
10828      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10829      * (e.g., during paging).
10830      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10831      */
10832     getModifiedRecords : function(){
10833         return this.modified;
10834     },
10835
10836     // private
10837     createFilterFn : function(property, value, anyMatch){
10838         if(!value.exec){ // not a regex
10839             value = String(value);
10840             if(value.length == 0){
10841                 return false;
10842             }
10843             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10844         }
10845         return function(r){
10846             return value.test(r.data[property]);
10847         };
10848     },
10849
10850     /**
10851      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10852      * @param {String} property A field on your records
10853      * @param {Number} start The record index to start at (defaults to 0)
10854      * @param {Number} end The last record index to include (defaults to length - 1)
10855      * @return {Number} The sum
10856      */
10857     sum : function(property, start, end){
10858         var rs = this.data.items, v = 0;
10859         start = start || 0;
10860         end = (end || end === 0) ? end : rs.length-1;
10861
10862         for(var i = start; i <= end; i++){
10863             v += (rs[i].data[property] || 0);
10864         }
10865         return v;
10866     },
10867
10868     /**
10869      * Filter the records by a specified property.
10870      * @param {String} field A field on your records
10871      * @param {String/RegExp} value Either a string that the field
10872      * should start with or a RegExp to test against the field
10873      * @param {Boolean} anyMatch True to match any part not just the beginning
10874      */
10875     filter : function(property, value, anyMatch){
10876         var fn = this.createFilterFn(property, value, anyMatch);
10877         return fn ? this.filterBy(fn) : this.clearFilter();
10878     },
10879
10880     /**
10881      * Filter by a function. The specified function will be called with each
10882      * record in this data source. If the function returns true the record is included,
10883      * otherwise it is filtered.
10884      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10885      * @param {Object} scope (optional) The scope of the function (defaults to this)
10886      */
10887     filterBy : function(fn, scope){
10888         this.snapshot = this.snapshot || this.data;
10889         this.data = this.queryBy(fn, scope||this);
10890         this.fireEvent("datachanged", this);
10891     },
10892
10893     /**
10894      * Query the records by a specified property.
10895      * @param {String} field A field on your records
10896      * @param {String/RegExp} value Either a string that the field
10897      * should start with or a RegExp to test against the field
10898      * @param {Boolean} anyMatch True to match any part not just the beginning
10899      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10900      */
10901     query : function(property, value, anyMatch){
10902         var fn = this.createFilterFn(property, value, anyMatch);
10903         return fn ? this.queryBy(fn) : this.data.clone();
10904     },
10905
10906     /**
10907      * Query by a function. The specified function will be called with each
10908      * record in this data source. If the function returns true the record is included
10909      * in the results.
10910      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10911      * @param {Object} scope (optional) The scope of the function (defaults to this)
10912       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10913      **/
10914     queryBy : function(fn, scope){
10915         var data = this.snapshot || this.data;
10916         return data.filterBy(fn, scope||this);
10917     },
10918
10919     /**
10920      * Collects unique values for a particular dataIndex from this store.
10921      * @param {String} dataIndex The property to collect
10922      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10923      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10924      * @return {Array} An array of the unique values
10925      **/
10926     collect : function(dataIndex, allowNull, bypassFilter){
10927         var d = (bypassFilter === true && this.snapshot) ?
10928                 this.snapshot.items : this.data.items;
10929         var v, sv, r = [], l = {};
10930         for(var i = 0, len = d.length; i < len; i++){
10931             v = d[i].data[dataIndex];
10932             sv = String(v);
10933             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10934                 l[sv] = true;
10935                 r[r.length] = v;
10936             }
10937         }
10938         return r;
10939     },
10940
10941     /**
10942      * Revert to a view of the Record cache with no filtering applied.
10943      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10944      */
10945     clearFilter : function(suppressEvent){
10946         if(this.snapshot && this.snapshot != this.data){
10947             this.data = this.snapshot;
10948             delete this.snapshot;
10949             if(suppressEvent !== true){
10950                 this.fireEvent("datachanged", this);
10951             }
10952         }
10953     },
10954
10955     // private
10956     afterEdit : function(record){
10957         if(this.modified.indexOf(record) == -1){
10958             this.modified.push(record);
10959         }
10960         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10961     },
10962     
10963     // private
10964     afterReject : function(record){
10965         this.modified.remove(record);
10966         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10967     },
10968
10969     // private
10970     afterCommit : function(record){
10971         this.modified.remove(record);
10972         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10973     },
10974
10975     /**
10976      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10977      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10978      */
10979     commitChanges : function(){
10980         var m = this.modified.slice(0);
10981         this.modified = [];
10982         for(var i = 0, len = m.length; i < len; i++){
10983             m[i].commit();
10984         }
10985     },
10986
10987     /**
10988      * Cancel outstanding changes on all changed records.
10989      */
10990     rejectChanges : function(){
10991         var m = this.modified.slice(0);
10992         this.modified = [];
10993         for(var i = 0, len = m.length; i < len; i++){
10994             m[i].reject();
10995         }
10996     },
10997
10998     onMetaChange : function(meta, rtype, o){
10999         this.recordType = rtype;
11000         this.fields = rtype.prototype.fields;
11001         delete this.snapshot;
11002         this.sortInfo = meta.sortInfo || this.sortInfo;
11003         this.modified = [];
11004         this.fireEvent('metachange', this, this.reader.meta);
11005     },
11006     
11007     moveIndex : function(data, type)
11008     {
11009         var index = this.indexOf(data);
11010         
11011         var newIndex = index + type;
11012         
11013         this.remove(data);
11014         
11015         this.insert(newIndex, data);
11016         
11017     }
11018 });/*
11019  * Based on:
11020  * Ext JS Library 1.1.1
11021  * Copyright(c) 2006-2007, Ext JS, LLC.
11022  *
11023  * Originally Released Under LGPL - original licence link has changed is not relivant.
11024  *
11025  * Fork - LGPL
11026  * <script type="text/javascript">
11027  */
11028
11029 /**
11030  * @class Roo.data.SimpleStore
11031  * @extends Roo.data.Store
11032  * Small helper class to make creating Stores from Array data easier.
11033  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11034  * @cfg {Array} fields An array of field definition objects, or field name strings.
11035  * @cfg {Array} data The multi-dimensional array of data
11036  * @constructor
11037  * @param {Object} config
11038  */
11039 Roo.data.SimpleStore = function(config){
11040     Roo.data.SimpleStore.superclass.constructor.call(this, {
11041         isLocal : true,
11042         reader: new Roo.data.ArrayReader({
11043                 id: config.id
11044             },
11045             Roo.data.Record.create(config.fields)
11046         ),
11047         proxy : new Roo.data.MemoryProxy(config.data)
11048     });
11049     this.load();
11050 };
11051 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11052  * Based on:
11053  * Ext JS Library 1.1.1
11054  * Copyright(c) 2006-2007, Ext JS, LLC.
11055  *
11056  * Originally Released Under LGPL - original licence link has changed is not relivant.
11057  *
11058  * Fork - LGPL
11059  * <script type="text/javascript">
11060  */
11061
11062 /**
11063 /**
11064  * @extends Roo.data.Store
11065  * @class Roo.data.JsonStore
11066  * Small helper class to make creating Stores for JSON data easier. <br/>
11067 <pre><code>
11068 var store = new Roo.data.JsonStore({
11069     url: 'get-images.php',
11070     root: 'images',
11071     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11072 });
11073 </code></pre>
11074  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11075  * JsonReader and HttpProxy (unless inline data is provided).</b>
11076  * @cfg {Array} fields An array of field definition objects, or field name strings.
11077  * @constructor
11078  * @param {Object} config
11079  */
11080 Roo.data.JsonStore = function(c){
11081     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11082         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11083         reader: new Roo.data.JsonReader(c, c.fields)
11084     }));
11085 };
11086 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11087  * Based on:
11088  * Ext JS Library 1.1.1
11089  * Copyright(c) 2006-2007, Ext JS, LLC.
11090  *
11091  * Originally Released Under LGPL - original licence link has changed is not relivant.
11092  *
11093  * Fork - LGPL
11094  * <script type="text/javascript">
11095  */
11096
11097  
11098 Roo.data.Field = function(config){
11099     if(typeof config == "string"){
11100         config = {name: config};
11101     }
11102     Roo.apply(this, config);
11103     
11104     if(!this.type){
11105         this.type = "auto";
11106     }
11107     
11108     var st = Roo.data.SortTypes;
11109     // named sortTypes are supported, here we look them up
11110     if(typeof this.sortType == "string"){
11111         this.sortType = st[this.sortType];
11112     }
11113     
11114     // set default sortType for strings and dates
11115     if(!this.sortType){
11116         switch(this.type){
11117             case "string":
11118                 this.sortType = st.asUCString;
11119                 break;
11120             case "date":
11121                 this.sortType = st.asDate;
11122                 break;
11123             default:
11124                 this.sortType = st.none;
11125         }
11126     }
11127
11128     // define once
11129     var stripRe = /[\$,%]/g;
11130
11131     // prebuilt conversion function for this field, instead of
11132     // switching every time we're reading a value
11133     if(!this.convert){
11134         var cv, dateFormat = this.dateFormat;
11135         switch(this.type){
11136             case "":
11137             case "auto":
11138             case undefined:
11139                 cv = function(v){ return v; };
11140                 break;
11141             case "string":
11142                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11143                 break;
11144             case "int":
11145                 cv = function(v){
11146                     return v !== undefined && v !== null && v !== '' ?
11147                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11148                     };
11149                 break;
11150             case "float":
11151                 cv = function(v){
11152                     return v !== undefined && v !== null && v !== '' ?
11153                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11154                     };
11155                 break;
11156             case "bool":
11157             case "boolean":
11158                 cv = function(v){ return v === true || v === "true" || v == 1; };
11159                 break;
11160             case "date":
11161                 cv = function(v){
11162                     if(!v){
11163                         return '';
11164                     }
11165                     if(v instanceof Date){
11166                         return v;
11167                     }
11168                     if(dateFormat){
11169                         if(dateFormat == "timestamp"){
11170                             return new Date(v*1000);
11171                         }
11172                         return Date.parseDate(v, dateFormat);
11173                     }
11174                     var parsed = Date.parse(v);
11175                     return parsed ? new Date(parsed) : null;
11176                 };
11177              break;
11178             
11179         }
11180         this.convert = cv;
11181     }
11182 };
11183
11184 Roo.data.Field.prototype = {
11185     dateFormat: null,
11186     defaultValue: "",
11187     mapping: null,
11188     sortType : null,
11189     sortDir : "ASC"
11190 };/*
11191  * Based on:
11192  * Ext JS Library 1.1.1
11193  * Copyright(c) 2006-2007, Ext JS, LLC.
11194  *
11195  * Originally Released Under LGPL - original licence link has changed is not relivant.
11196  *
11197  * Fork - LGPL
11198  * <script type="text/javascript">
11199  */
11200  
11201 // Base class for reading structured data from a data source.  This class is intended to be
11202 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11203
11204 /**
11205  * @class Roo.data.DataReader
11206  * Base class for reading structured data from a data source.  This class is intended to be
11207  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11208  */
11209
11210 Roo.data.DataReader = function(meta, recordType){
11211     
11212     this.meta = meta;
11213     
11214     this.recordType = recordType instanceof Array ? 
11215         Roo.data.Record.create(recordType) : recordType;
11216 };
11217
11218 Roo.data.DataReader.prototype = {
11219      /**
11220      * Create an empty record
11221      * @param {Object} data (optional) - overlay some values
11222      * @return {Roo.data.Record} record created.
11223      */
11224     newRow :  function(d) {
11225         var da =  {};
11226         this.recordType.prototype.fields.each(function(c) {
11227             switch( c.type) {
11228                 case 'int' : da[c.name] = 0; break;
11229                 case 'date' : da[c.name] = new Date(); break;
11230                 case 'float' : da[c.name] = 0.0; break;
11231                 case 'boolean' : da[c.name] = false; break;
11232                 default : da[c.name] = ""; break;
11233             }
11234             
11235         });
11236         return new this.recordType(Roo.apply(da, d));
11237     }
11238     
11239 };/*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250 /**
11251  * @class Roo.data.DataProxy
11252  * @extends Roo.data.Observable
11253  * This class is an abstract base class for implementations which provide retrieval of
11254  * unformatted data objects.<br>
11255  * <p>
11256  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11257  * (of the appropriate type which knows how to parse the data object) to provide a block of
11258  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11259  * <p>
11260  * Custom implementations must implement the load method as described in
11261  * {@link Roo.data.HttpProxy#load}.
11262  */
11263 Roo.data.DataProxy = function(){
11264     this.addEvents({
11265         /**
11266          * @event beforeload
11267          * Fires before a network request is made to retrieve a data object.
11268          * @param {Object} This DataProxy object.
11269          * @param {Object} params The params parameter to the load function.
11270          */
11271         beforeload : true,
11272         /**
11273          * @event load
11274          * Fires before the load method's callback is called.
11275          * @param {Object} This DataProxy object.
11276          * @param {Object} o The data object.
11277          * @param {Object} arg The callback argument object passed to the load function.
11278          */
11279         load : true,
11280         /**
11281          * @event loadexception
11282          * Fires if an Exception occurs during data retrieval.
11283          * @param {Object} This DataProxy object.
11284          * @param {Object} o The data object.
11285          * @param {Object} arg The callback argument object passed to the load function.
11286          * @param {Object} e The Exception.
11287          */
11288         loadexception : true
11289     });
11290     Roo.data.DataProxy.superclass.constructor.call(this);
11291 };
11292
11293 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11294
11295     /**
11296      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11297      */
11298 /*
11299  * Based on:
11300  * Ext JS Library 1.1.1
11301  * Copyright(c) 2006-2007, Ext JS, LLC.
11302  *
11303  * Originally Released Under LGPL - original licence link has changed is not relivant.
11304  *
11305  * Fork - LGPL
11306  * <script type="text/javascript">
11307  */
11308 /**
11309  * @class Roo.data.MemoryProxy
11310  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11311  * to the Reader when its load method is called.
11312  * @constructor
11313  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11314  */
11315 Roo.data.MemoryProxy = function(data){
11316     if (data.data) {
11317         data = data.data;
11318     }
11319     Roo.data.MemoryProxy.superclass.constructor.call(this);
11320     this.data = data;
11321 };
11322
11323 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11324     
11325     /**
11326      * Load data from the requested source (in this case an in-memory
11327      * data object passed to the constructor), read the data object into
11328      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11329      * process that block using the passed callback.
11330      * @param {Object} params This parameter is not used by the MemoryProxy class.
11331      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11332      * object into a block of Roo.data.Records.
11333      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11334      * The function must be passed <ul>
11335      * <li>The Record block object</li>
11336      * <li>The "arg" argument from the load function</li>
11337      * <li>A boolean success indicator</li>
11338      * </ul>
11339      * @param {Object} scope The scope in which to call the callback
11340      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11341      */
11342     load : function(params, reader, callback, scope, arg){
11343         params = params || {};
11344         var result;
11345         try {
11346             result = reader.readRecords(this.data);
11347         }catch(e){
11348             this.fireEvent("loadexception", this, arg, null, e);
11349             callback.call(scope, null, arg, false);
11350             return;
11351         }
11352         callback.call(scope, result, arg, true);
11353     },
11354     
11355     // private
11356     update : function(params, records){
11357         
11358     }
11359 });/*
11360  * Based on:
11361  * Ext JS Library 1.1.1
11362  * Copyright(c) 2006-2007, Ext JS, LLC.
11363  *
11364  * Originally Released Under LGPL - original licence link has changed is not relivant.
11365  *
11366  * Fork - LGPL
11367  * <script type="text/javascript">
11368  */
11369 /**
11370  * @class Roo.data.HttpProxy
11371  * @extends Roo.data.DataProxy
11372  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11373  * configured to reference a certain URL.<br><br>
11374  * <p>
11375  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11376  * from which the running page was served.<br><br>
11377  * <p>
11378  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11379  * <p>
11380  * Be aware that to enable the browser to parse an XML document, the server must set
11381  * the Content-Type header in the HTTP response to "text/xml".
11382  * @constructor
11383  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11384  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11385  * will be used to make the request.
11386  */
11387 Roo.data.HttpProxy = function(conn){
11388     Roo.data.HttpProxy.superclass.constructor.call(this);
11389     // is conn a conn config or a real conn?
11390     this.conn = conn;
11391     this.useAjax = !conn || !conn.events;
11392   
11393 };
11394
11395 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11396     // thse are take from connection...
11397     
11398     /**
11399      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11400      */
11401     /**
11402      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11403      * extra parameters to each request made by this object. (defaults to undefined)
11404      */
11405     /**
11406      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11407      *  to each request made by this object. (defaults to undefined)
11408      */
11409     /**
11410      * @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)
11411      */
11412     /**
11413      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11414      */
11415      /**
11416      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11417      * @type Boolean
11418      */
11419   
11420
11421     /**
11422      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11423      * @type Boolean
11424      */
11425     /**
11426      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11427      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11428      * a finer-grained basis than the DataProxy events.
11429      */
11430     getConnection : function(){
11431         return this.useAjax ? Roo.Ajax : this.conn;
11432     },
11433
11434     /**
11435      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11436      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11437      * process that block using the passed callback.
11438      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11439      * for the request to the remote server.
11440      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11441      * object into a block of Roo.data.Records.
11442      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11443      * The function must be passed <ul>
11444      * <li>The Record block object</li>
11445      * <li>The "arg" argument from the load function</li>
11446      * <li>A boolean success indicator</li>
11447      * </ul>
11448      * @param {Object} scope The scope in which to call the callback
11449      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11450      */
11451     load : function(params, reader, callback, scope, arg){
11452         if(this.fireEvent("beforeload", this, params) !== false){
11453             var  o = {
11454                 params : params || {},
11455                 request: {
11456                     callback : callback,
11457                     scope : scope,
11458                     arg : arg
11459                 },
11460                 reader: reader,
11461                 callback : this.loadResponse,
11462                 scope: this
11463             };
11464             if(this.useAjax){
11465                 Roo.applyIf(o, this.conn);
11466                 if(this.activeRequest){
11467                     Roo.Ajax.abort(this.activeRequest);
11468                 }
11469                 this.activeRequest = Roo.Ajax.request(o);
11470             }else{
11471                 this.conn.request(o);
11472             }
11473         }else{
11474             callback.call(scope||this, null, arg, false);
11475         }
11476     },
11477
11478     // private
11479     loadResponse : function(o, success, response){
11480         delete this.activeRequest;
11481         if(!success){
11482             this.fireEvent("loadexception", this, o, response);
11483             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11484             return;
11485         }
11486         var result;
11487         try {
11488             result = o.reader.read(response);
11489         }catch(e){
11490             this.fireEvent("loadexception", this, o, response, e);
11491             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11492             return;
11493         }
11494         
11495         this.fireEvent("load", this, o, o.request.arg);
11496         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11497     },
11498
11499     // private
11500     update : function(dataSet){
11501
11502     },
11503
11504     // private
11505     updateResponse : function(dataSet){
11506
11507     }
11508 });/*
11509  * Based on:
11510  * Ext JS Library 1.1.1
11511  * Copyright(c) 2006-2007, Ext JS, LLC.
11512  *
11513  * Originally Released Under LGPL - original licence link has changed is not relivant.
11514  *
11515  * Fork - LGPL
11516  * <script type="text/javascript">
11517  */
11518
11519 /**
11520  * @class Roo.data.ScriptTagProxy
11521  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11522  * other than the originating domain of the running page.<br><br>
11523  * <p>
11524  * <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
11525  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11526  * <p>
11527  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11528  * source code that is used as the source inside a &lt;script> tag.<br><br>
11529  * <p>
11530  * In order for the browser to process the returned data, the server must wrap the data object
11531  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11532  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11533  * depending on whether the callback name was passed:
11534  * <p>
11535  * <pre><code>
11536 boolean scriptTag = false;
11537 String cb = request.getParameter("callback");
11538 if (cb != null) {
11539     scriptTag = true;
11540     response.setContentType("text/javascript");
11541 } else {
11542     response.setContentType("application/x-json");
11543 }
11544 Writer out = response.getWriter();
11545 if (scriptTag) {
11546     out.write(cb + "(");
11547 }
11548 out.print(dataBlock.toJsonString());
11549 if (scriptTag) {
11550     out.write(");");
11551 }
11552 </pre></code>
11553  *
11554  * @constructor
11555  * @param {Object} config A configuration object.
11556  */
11557 Roo.data.ScriptTagProxy = function(config){
11558     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11559     Roo.apply(this, config);
11560     this.head = document.getElementsByTagName("head")[0];
11561 };
11562
11563 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11564
11565 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11566     /**
11567      * @cfg {String} url The URL from which to request the data object.
11568      */
11569     /**
11570      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11571      */
11572     timeout : 30000,
11573     /**
11574      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11575      * the server the name of the callback function set up by the load call to process the returned data object.
11576      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11577      * javascript output which calls this named function passing the data object as its only parameter.
11578      */
11579     callbackParam : "callback",
11580     /**
11581      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11582      * name to the request.
11583      */
11584     nocache : true,
11585
11586     /**
11587      * Load data from the configured URL, read the data object into
11588      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11589      * process that block using the passed callback.
11590      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11591      * for the request to the remote server.
11592      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11593      * object into a block of Roo.data.Records.
11594      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11595      * The function must be passed <ul>
11596      * <li>The Record block object</li>
11597      * <li>The "arg" argument from the load function</li>
11598      * <li>A boolean success indicator</li>
11599      * </ul>
11600      * @param {Object} scope The scope in which to call the callback
11601      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11602      */
11603     load : function(params, reader, callback, scope, arg){
11604         if(this.fireEvent("beforeload", this, params) !== false){
11605
11606             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11607
11608             var url = this.url;
11609             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11610             if(this.nocache){
11611                 url += "&_dc=" + (new Date().getTime());
11612             }
11613             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11614             var trans = {
11615                 id : transId,
11616                 cb : "stcCallback"+transId,
11617                 scriptId : "stcScript"+transId,
11618                 params : params,
11619                 arg : arg,
11620                 url : url,
11621                 callback : callback,
11622                 scope : scope,
11623                 reader : reader
11624             };
11625             var conn = this;
11626
11627             window[trans.cb] = function(o){
11628                 conn.handleResponse(o, trans);
11629             };
11630
11631             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11632
11633             if(this.autoAbort !== false){
11634                 this.abort();
11635             }
11636
11637             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11638
11639             var script = document.createElement("script");
11640             script.setAttribute("src", url);
11641             script.setAttribute("type", "text/javascript");
11642             script.setAttribute("id", trans.scriptId);
11643             this.head.appendChild(script);
11644
11645             this.trans = trans;
11646         }else{
11647             callback.call(scope||this, null, arg, false);
11648         }
11649     },
11650
11651     // private
11652     isLoading : function(){
11653         return this.trans ? true : false;
11654     },
11655
11656     /**
11657      * Abort the current server request.
11658      */
11659     abort : function(){
11660         if(this.isLoading()){
11661             this.destroyTrans(this.trans);
11662         }
11663     },
11664
11665     // private
11666     destroyTrans : function(trans, isLoaded){
11667         this.head.removeChild(document.getElementById(trans.scriptId));
11668         clearTimeout(trans.timeoutId);
11669         if(isLoaded){
11670             window[trans.cb] = undefined;
11671             try{
11672                 delete window[trans.cb];
11673             }catch(e){}
11674         }else{
11675             // if hasn't been loaded, wait for load to remove it to prevent script error
11676             window[trans.cb] = function(){
11677                 window[trans.cb] = undefined;
11678                 try{
11679                     delete window[trans.cb];
11680                 }catch(e){}
11681             };
11682         }
11683     },
11684
11685     // private
11686     handleResponse : function(o, trans){
11687         this.trans = false;
11688         this.destroyTrans(trans, true);
11689         var result;
11690         try {
11691             result = trans.reader.readRecords(o);
11692         }catch(e){
11693             this.fireEvent("loadexception", this, o, trans.arg, e);
11694             trans.callback.call(trans.scope||window, null, trans.arg, false);
11695             return;
11696         }
11697         this.fireEvent("load", this, o, trans.arg);
11698         trans.callback.call(trans.scope||window, result, trans.arg, true);
11699     },
11700
11701     // private
11702     handleFailure : function(trans){
11703         this.trans = false;
11704         this.destroyTrans(trans, false);
11705         this.fireEvent("loadexception", this, null, trans.arg);
11706         trans.callback.call(trans.scope||window, null, trans.arg, false);
11707     }
11708 });/*
11709  * Based on:
11710  * Ext JS Library 1.1.1
11711  * Copyright(c) 2006-2007, Ext JS, LLC.
11712  *
11713  * Originally Released Under LGPL - original licence link has changed is not relivant.
11714  *
11715  * Fork - LGPL
11716  * <script type="text/javascript">
11717  */
11718
11719 /**
11720  * @class Roo.data.JsonReader
11721  * @extends Roo.data.DataReader
11722  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11723  * based on mappings in a provided Roo.data.Record constructor.
11724  * 
11725  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11726  * in the reply previously. 
11727  * 
11728  * <p>
11729  * Example code:
11730  * <pre><code>
11731 var RecordDef = Roo.data.Record.create([
11732     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11733     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11734 ]);
11735 var myReader = new Roo.data.JsonReader({
11736     totalProperty: "results",    // The property which contains the total dataset size (optional)
11737     root: "rows",                // The property which contains an Array of row objects
11738     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11739 }, RecordDef);
11740 </code></pre>
11741  * <p>
11742  * This would consume a JSON file like this:
11743  * <pre><code>
11744 { 'results': 2, 'rows': [
11745     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11746     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11747 }
11748 </code></pre>
11749  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11750  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11751  * paged from the remote server.
11752  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11753  * @cfg {String} root name of the property which contains the Array of row objects.
11754  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11755  * @cfg {Array} fields Array of field definition objects
11756  * @constructor
11757  * Create a new JsonReader
11758  * @param {Object} meta Metadata configuration options
11759  * @param {Object} recordType Either an Array of field definition objects,
11760  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11761  */
11762 Roo.data.JsonReader = function(meta, recordType){
11763     
11764     meta = meta || {};
11765     // set some defaults:
11766     Roo.applyIf(meta, {
11767         totalProperty: 'total',
11768         successProperty : 'success',
11769         root : 'data',
11770         id : 'id'
11771     });
11772     
11773     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11774 };
11775 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11776     
11777     /**
11778      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11779      * Used by Store query builder to append _requestMeta to params.
11780      * 
11781      */
11782     metaFromRemote : false,
11783     /**
11784      * This method is only used by a DataProxy which has retrieved data from a remote server.
11785      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11786      * @return {Object} data A data block which is used by an Roo.data.Store object as
11787      * a cache of Roo.data.Records.
11788      */
11789     read : function(response){
11790         var json = response.responseText;
11791        
11792         var o = /* eval:var:o */ eval("("+json+")");
11793         if(!o) {
11794             throw {message: "JsonReader.read: Json object not found"};
11795         }
11796         
11797         if(o.metaData){
11798             
11799             delete this.ef;
11800             this.metaFromRemote = true;
11801             this.meta = o.metaData;
11802             this.recordType = Roo.data.Record.create(o.metaData.fields);
11803             this.onMetaChange(this.meta, this.recordType, o);
11804         }
11805         return this.readRecords(o);
11806     },
11807
11808     // private function a store will implement
11809     onMetaChange : function(meta, recordType, o){
11810
11811     },
11812
11813     /**
11814          * @ignore
11815          */
11816     simpleAccess: function(obj, subsc) {
11817         return obj[subsc];
11818     },
11819
11820         /**
11821          * @ignore
11822          */
11823     getJsonAccessor: function(){
11824         var re = /[\[\.]/;
11825         return function(expr) {
11826             try {
11827                 return(re.test(expr))
11828                     ? new Function("obj", "return obj." + expr)
11829                     : function(obj){
11830                         return obj[expr];
11831                     };
11832             } catch(e){}
11833             return Roo.emptyFn;
11834         };
11835     }(),
11836
11837     /**
11838      * Create a data block containing Roo.data.Records from an XML document.
11839      * @param {Object} o An object which contains an Array of row objects in the property specified
11840      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11841      * which contains the total size of the dataset.
11842      * @return {Object} data A data block which is used by an Roo.data.Store object as
11843      * a cache of Roo.data.Records.
11844      */
11845     readRecords : function(o){
11846         /**
11847          * After any data loads, the raw JSON data is available for further custom processing.
11848          * @type Object
11849          */
11850         this.o = o;
11851         var s = this.meta, Record = this.recordType,
11852             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11853
11854 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11855         if (!this.ef) {
11856             if(s.totalProperty) {
11857                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11858                 }
11859                 if(s.successProperty) {
11860                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11861                 }
11862                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11863                 if (s.id) {
11864                         var g = this.getJsonAccessor(s.id);
11865                         this.getId = function(rec) {
11866                                 var r = g(rec);  
11867                                 return (r === undefined || r === "") ? null : r;
11868                         };
11869                 } else {
11870                         this.getId = function(){return null;};
11871                 }
11872             this.ef = [];
11873             for(var jj = 0; jj < fl; jj++){
11874                 f = fi[jj];
11875                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11876                 this.ef[jj] = this.getJsonAccessor(map);
11877             }
11878         }
11879
11880         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11881         if(s.totalProperty){
11882             var vt = parseInt(this.getTotal(o), 10);
11883             if(!isNaN(vt)){
11884                 totalRecords = vt;
11885             }
11886         }
11887         if(s.successProperty){
11888             var vs = this.getSuccess(o);
11889             if(vs === false || vs === 'false'){
11890                 success = false;
11891             }
11892         }
11893         var records = [];
11894         for(var i = 0; i < c; i++){
11895                 var n = root[i];
11896             var values = {};
11897             var id = this.getId(n);
11898             for(var j = 0; j < fl; j++){
11899                 f = fi[j];
11900             var v = this.ef[j](n);
11901             if (!f.convert) {
11902                 Roo.log('missing convert for ' + f.name);
11903                 Roo.log(f);
11904                 continue;
11905             }
11906             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11907             }
11908             var record = new Record(values, id);
11909             record.json = n;
11910             records[i] = record;
11911         }
11912         return {
11913             raw : o,
11914             success : success,
11915             records : records,
11916             totalRecords : totalRecords
11917         };
11918     }
11919 });/*
11920  * Based on:
11921  * Ext JS Library 1.1.1
11922  * Copyright(c) 2006-2007, Ext JS, LLC.
11923  *
11924  * Originally Released Under LGPL - original licence link has changed is not relivant.
11925  *
11926  * Fork - LGPL
11927  * <script type="text/javascript">
11928  */
11929
11930 /**
11931  * @class Roo.data.ArrayReader
11932  * @extends Roo.data.DataReader
11933  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11934  * Each element of that Array represents a row of data fields. The
11935  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11936  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11937  * <p>
11938  * Example code:.
11939  * <pre><code>
11940 var RecordDef = Roo.data.Record.create([
11941     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11942     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11943 ]);
11944 var myReader = new Roo.data.ArrayReader({
11945     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11946 }, RecordDef);
11947 </code></pre>
11948  * <p>
11949  * This would consume an Array like this:
11950  * <pre><code>
11951 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11952   </code></pre>
11953  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11954  * @constructor
11955  * Create a new JsonReader
11956  * @param {Object} meta Metadata configuration options.
11957  * @param {Object} recordType Either an Array of field definition objects
11958  * as specified to {@link Roo.data.Record#create},
11959  * or an {@link Roo.data.Record} object
11960  * created using {@link Roo.data.Record#create}.
11961  */
11962 Roo.data.ArrayReader = function(meta, recordType){
11963     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11964 };
11965
11966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11967     /**
11968      * Create a data block containing Roo.data.Records from an XML document.
11969      * @param {Object} o An Array of row objects which represents the dataset.
11970      * @return {Object} data A data block which is used by an Roo.data.Store object as
11971      * a cache of Roo.data.Records.
11972      */
11973     readRecords : function(o){
11974         var sid = this.meta ? this.meta.id : null;
11975         var recordType = this.recordType, fields = recordType.prototype.fields;
11976         var records = [];
11977         var root = o;
11978             for(var i = 0; i < root.length; i++){
11979                     var n = root[i];
11980                 var values = {};
11981                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11982                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11983                 var f = fields.items[j];
11984                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11985                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11986                 v = f.convert(v);
11987                 values[f.name] = v;
11988             }
11989                 var record = new recordType(values, id);
11990                 record.json = n;
11991                 records[records.length] = record;
11992             }
11993             return {
11994                 records : records,
11995                 totalRecords : records.length
11996             };
11997     }
11998 });/*
11999  * - LGPL
12000  * * 
12001  */
12002
12003 /**
12004  * @class Roo.bootstrap.ComboBox
12005  * @extends Roo.bootstrap.TriggerField
12006  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12007  * @cfg {Boolean} append (true|false) default false
12008  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12009  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12010  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12011  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12012  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12013  * @cfg {Boolean} animate default true
12014  * @cfg {Boolean} emptyResultText only for touch device
12015  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12016  * @constructor
12017  * Create a new ComboBox.
12018  * @param {Object} config Configuration options
12019  */
12020 Roo.bootstrap.ComboBox = function(config){
12021     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12022     this.addEvents({
12023         /**
12024          * @event expand
12025          * Fires when the dropdown list is expanded
12026              * @param {Roo.bootstrap.ComboBox} combo This combo box
12027              */
12028         'expand' : true,
12029         /**
12030          * @event collapse
12031          * Fires when the dropdown list is collapsed
12032              * @param {Roo.bootstrap.ComboBox} combo This combo box
12033              */
12034         'collapse' : true,
12035         /**
12036          * @event beforeselect
12037          * Fires before a list item is selected. Return false to cancel the selection.
12038              * @param {Roo.bootstrap.ComboBox} combo This combo box
12039              * @param {Roo.data.Record} record The data record returned from the underlying store
12040              * @param {Number} index The index of the selected item in the dropdown list
12041              */
12042         'beforeselect' : true,
12043         /**
12044          * @event select
12045          * Fires when a list item is selected
12046              * @param {Roo.bootstrap.ComboBox} combo This combo box
12047              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12048              * @param {Number} index The index of the selected item in the dropdown list
12049              */
12050         'select' : true,
12051         /**
12052          * @event beforequery
12053          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12054          * The event object passed has these properties:
12055              * @param {Roo.bootstrap.ComboBox} combo This combo box
12056              * @param {String} query The query
12057              * @param {Boolean} forceAll true to force "all" query
12058              * @param {Boolean} cancel true to cancel the query
12059              * @param {Object} e The query event object
12060              */
12061         'beforequery': true,
12062          /**
12063          * @event add
12064          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12065              * @param {Roo.bootstrap.ComboBox} combo This combo box
12066              */
12067         'add' : true,
12068         /**
12069          * @event edit
12070          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12071              * @param {Roo.bootstrap.ComboBox} combo This combo box
12072              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12073              */
12074         'edit' : true,
12075         /**
12076          * @event remove
12077          * Fires when the remove value from the combobox array
12078              * @param {Roo.bootstrap.ComboBox} combo This combo box
12079              */
12080         'remove' : true,
12081         /**
12082          * @event afterremove
12083          * Fires when the remove value from the combobox array
12084              * @param {Roo.bootstrap.ComboBox} combo This combo box
12085              */
12086         'afterremove' : true,
12087         /**
12088          * @event specialfilter
12089          * Fires when specialfilter
12090             * @param {Roo.bootstrap.ComboBox} combo This combo box
12091             */
12092         'specialfilter' : true,
12093         /**
12094          * @event tick
12095          * Fires when tick the element
12096             * @param {Roo.bootstrap.ComboBox} combo This combo box
12097             */
12098         'tick' : true,
12099         /**
12100          * @event touchviewdisplay
12101          * Fires when touch view require special display (default is using displayField)
12102             * @param {Roo.bootstrap.ComboBox} combo This combo box
12103             * @param {Object} cfg set html .
12104             */
12105         'touchviewdisplay' : true
12106         
12107     });
12108     
12109     this.item = [];
12110     this.tickItems = [];
12111     
12112     this.selectedIndex = -1;
12113     if(this.mode == 'local'){
12114         if(config.queryDelay === undefined){
12115             this.queryDelay = 10;
12116         }
12117         if(config.minChars === undefined){
12118             this.minChars = 0;
12119         }
12120     }
12121 };
12122
12123 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12124      
12125     /**
12126      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12127      * rendering into an Roo.Editor, defaults to false)
12128      */
12129     /**
12130      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12131      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12132      */
12133     /**
12134      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12135      */
12136     /**
12137      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12138      * the dropdown list (defaults to undefined, with no header element)
12139      */
12140
12141      /**
12142      * @cfg {String/Roo.Template} tpl The template to use to render the output
12143      */
12144      
12145      /**
12146      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12147      */
12148     listWidth: undefined,
12149     /**
12150      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12151      * mode = 'remote' or 'text' if mode = 'local')
12152      */
12153     displayField: undefined,
12154     
12155     /**
12156      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12157      * mode = 'remote' or 'value' if mode = 'local'). 
12158      * Note: use of a valueField requires the user make a selection
12159      * in order for a value to be mapped.
12160      */
12161     valueField: undefined,
12162     /**
12163      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12164      */
12165     modalTitle : '',
12166     
12167     /**
12168      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12169      * field's data value (defaults to the underlying DOM element's name)
12170      */
12171     hiddenName: undefined,
12172     /**
12173      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12174      */
12175     listClass: '',
12176     /**
12177      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12178      */
12179     selectedClass: 'active',
12180     
12181     /**
12182      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12183      */
12184     shadow:'sides',
12185     /**
12186      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12187      * anchor positions (defaults to 'tl-bl')
12188      */
12189     listAlign: 'tl-bl?',
12190     /**
12191      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12192      */
12193     maxHeight: 300,
12194     /**
12195      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12196      * query specified by the allQuery config option (defaults to 'query')
12197      */
12198     triggerAction: 'query',
12199     /**
12200      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12201      * (defaults to 4, does not apply if editable = false)
12202      */
12203     minChars : 4,
12204     /**
12205      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12206      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12207      */
12208     typeAhead: false,
12209     /**
12210      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12211      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12212      */
12213     queryDelay: 500,
12214     /**
12215      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12216      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12217      */
12218     pageSize: 0,
12219     /**
12220      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12221      * when editable = true (defaults to false)
12222      */
12223     selectOnFocus:false,
12224     /**
12225      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12226      */
12227     queryParam: 'query',
12228     /**
12229      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12230      * when mode = 'remote' (defaults to 'Loading...')
12231      */
12232     loadingText: 'Loading...',
12233     /**
12234      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12235      */
12236     resizable: false,
12237     /**
12238      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12239      */
12240     handleHeight : 8,
12241     /**
12242      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12243      * traditional select (defaults to true)
12244      */
12245     editable: true,
12246     /**
12247      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12248      */
12249     allQuery: '',
12250     /**
12251      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12252      */
12253     mode: 'remote',
12254     /**
12255      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12256      * listWidth has a higher value)
12257      */
12258     minListWidth : 70,
12259     /**
12260      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12261      * allow the user to set arbitrary text into the field (defaults to false)
12262      */
12263     forceSelection:false,
12264     /**
12265      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12266      * if typeAhead = true (defaults to 250)
12267      */
12268     typeAheadDelay : 250,
12269     /**
12270      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12271      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12272      */
12273     valueNotFoundText : undefined,
12274     /**
12275      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12276      */
12277     blockFocus : false,
12278     
12279     /**
12280      * @cfg {Boolean} disableClear Disable showing of clear button.
12281      */
12282     disableClear : false,
12283     /**
12284      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12285      */
12286     alwaysQuery : false,
12287     
12288     /**
12289      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12290      */
12291     multiple : false,
12292     
12293     /**
12294      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12295      */
12296     invalidClass : "has-warning",
12297     
12298     /**
12299      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12300      */
12301     validClass : "has-success",
12302     
12303     /**
12304      * @cfg {Boolean} specialFilter (true|false) special filter default false
12305      */
12306     specialFilter : false,
12307     
12308     /**
12309      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12310      */
12311     mobileTouchView : true,
12312     
12313     /**
12314      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12315      */
12316     useNativeIOS : false,
12317     
12318     ios_options : false,
12319     
12320     //private
12321     addicon : false,
12322     editicon: false,
12323     
12324     page: 0,
12325     hasQuery: false,
12326     append: false,
12327     loadNext: false,
12328     autoFocus : true,
12329     tickable : false,
12330     btnPosition : 'right',
12331     triggerList : true,
12332     showToggleBtn : true,
12333     animate : true,
12334     emptyResultText: 'Empty',
12335     triggerText : 'Select',
12336     
12337     // element that contains real text value.. (when hidden is used..)
12338     
12339     getAutoCreate : function()
12340     {
12341         var cfg = false;
12342         
12343         /*
12344          * Render classic select for iso
12345          */
12346         
12347         if(Roo.isIOS && this.useNativeIOS){
12348             cfg = this.getAutoCreateNativeIOS();
12349             return cfg;
12350         }
12351         
12352         /*
12353          * Touch Devices
12354          */
12355         
12356         if(Roo.isTouch && this.mobileTouchView){
12357             cfg = this.getAutoCreateTouchView();
12358             return cfg;;
12359         }
12360         
12361         /*
12362          *  Normal ComboBox
12363          */
12364         if(!this.tickable){
12365             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12366             return cfg;
12367         }
12368         
12369         /*
12370          *  ComboBox with tickable selections
12371          */
12372              
12373         var align = this.labelAlign || this.parentLabelAlign();
12374         
12375         cfg = {
12376             cls : 'form-group roo-combobox-tickable' //input-group
12377         };
12378         
12379         var btn_text_select = '';
12380         var btn_text_done = '';
12381         var btn_text_cancel = '';
12382         
12383         if (this.btn_text_show) {
12384             btn_text_select = 'Select';
12385             btn_text_done = 'Done';
12386             btn_text_cancel = 'Cancel'; 
12387         }
12388         
12389         var buttons = {
12390             tag : 'div',
12391             cls : 'tickable-buttons',
12392             cn : [
12393                 {
12394                     tag : 'button',
12395                     type : 'button',
12396                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12397                     //html : this.triggerText
12398                     html: btn_text_select
12399                 },
12400                 {
12401                     tag : 'button',
12402                     type : 'button',
12403                     name : 'ok',
12404                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12405                     //html : 'Done'
12406                     html: btn_text_done
12407                 },
12408                 {
12409                     tag : 'button',
12410                     type : 'button',
12411                     name : 'cancel',
12412                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12413                     //html : 'Cancel'
12414                     html: btn_text_cancel
12415                 }
12416             ]
12417         };
12418         
12419         if(this.editable){
12420             buttons.cn.unshift({
12421                 tag: 'input',
12422                 cls: 'roo-select2-search-field-input'
12423             });
12424         }
12425         
12426         var _this = this;
12427         
12428         Roo.each(buttons.cn, function(c){
12429             if (_this.size) {
12430                 c.cls += ' btn-' + _this.size;
12431             }
12432
12433             if (_this.disabled) {
12434                 c.disabled = true;
12435             }
12436         });
12437         
12438         var box = {
12439             tag: 'div',
12440             cn: [
12441                 {
12442                     tag: 'input',
12443                     type : 'hidden',
12444                     cls: 'form-hidden-field'
12445                 },
12446                 {
12447                     tag: 'ul',
12448                     cls: 'roo-select2-choices',
12449                     cn:[
12450                         {
12451                             tag: 'li',
12452                             cls: 'roo-select2-search-field',
12453                             cn: [
12454
12455                                 buttons
12456                             ]
12457                         }
12458                     ]
12459                 }
12460             ]
12461         };
12462         
12463         var combobox = {
12464             cls: 'roo-select2-container input-group roo-select2-container-multi',
12465             cn: [
12466                 box
12467 //                {
12468 //                    tag: 'ul',
12469 //                    cls: 'typeahead typeahead-long dropdown-menu',
12470 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12471 //                }
12472             ]
12473         };
12474         
12475         if(this.hasFeedback && !this.allowBlank){
12476             
12477             var feedback = {
12478                 tag: 'span',
12479                 cls: 'glyphicon form-control-feedback'
12480             };
12481
12482             combobox.cn.push(feedback);
12483         }
12484         
12485         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12486             
12487 //                Roo.log("left and has label");
12488             cfg.cn = [
12489                 {
12490                     tag : 'i',
12491                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12492                     tooltip : 'This field is required'
12493                 },
12494                 {
12495                     tag: 'label',
12496                     'for' :  id,
12497                     cls : 'control-label col-sm-' + this.labelWidth,
12498                     html : this.fieldLabel
12499
12500                 },
12501                 {
12502                     cls : "col-sm-" + (12 - this.labelWidth), 
12503                     cn: [
12504                         combobox
12505                     ]
12506                 }
12507
12508             ];
12509
12510             if(this.indicatorpos == 'right'){
12511                 
12512                 cfg.cn = [
12513                     {
12514                         tag: 'label',
12515                         'for' :  id,
12516                         cls : 'control-label col-sm-' + this.labelWidth,
12517                         html : this.fieldLabel
12518
12519                     },
12520                     {
12521                         tag : 'i',
12522                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12523                         tooltip : 'This field is required'
12524                     },
12525                     {
12526                         cls : "col-sm-" + (12 - this.labelWidth), 
12527                         cn: [
12528                             combobox
12529                         ]
12530                     }
12531
12532                 ];
12533             
12534             }
12535                 
12536                 
12537         } else if ( this.fieldLabel.length) {
12538 //                Roo.log(" label");
12539                  cfg.cn = [
12540                     {
12541                         tag : 'i',
12542                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12543                         tooltip : 'This field is required'
12544                     },
12545                     {
12546                         tag: 'label',
12547                         //cls : 'input-group-addon',
12548                         html : this.fieldLabel
12549                         
12550                     },
12551                     
12552                     combobox
12553                     
12554                 ];
12555                 
12556                 if(this.indicatorpos == 'right'){
12557                     
12558                     cfg.cn = [
12559                         {
12560                             tag: 'label',
12561                             //cls : 'input-group-addon',
12562                             html : this.fieldLabel
12563
12564                         },
12565                         
12566                         {
12567                             tag : 'i',
12568                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12569                             tooltip : 'This field is required'
12570                         },
12571                         
12572                         combobox
12573
12574                     ];
12575                 
12576                 }
12577
12578         } else {
12579             
12580 //                Roo.log(" no label && no align");
12581                 cfg = combobox
12582                      
12583                 
12584         }
12585          
12586         var settings=this;
12587         ['xs','sm','md','lg'].map(function(size){
12588             if (settings[size]) {
12589                 cfg.cls += ' col-' + size + '-' + settings[size];
12590             }
12591         });
12592         
12593         return cfg;
12594         
12595     },
12596     
12597     _initEventsCalled : false,
12598     
12599     // private
12600     initEvents: function()
12601     {   
12602         if (this._initEventsCalled) { // as we call render... prevent looping...
12603             return;
12604         }
12605         this._initEventsCalled = true;
12606         
12607         if (!this.store) {
12608             throw "can not find store for combo";
12609         }
12610         
12611         this.store = Roo.factory(this.store, Roo.data);
12612         
12613         // if we are building from html. then this element is so complex, that we can not really
12614         // use the rendered HTML.
12615         // so we have to trash and replace the previous code.
12616         if (Roo.XComponent.build_from_html) {
12617             
12618             // remove this element....
12619             var e = this.el.dom, k=0;
12620             while (e ) { e = e.previousSibling;  ++k;}
12621
12622             this.el.remove();
12623             
12624             this.el=false;
12625             this.rendered = false;
12626             
12627             this.render(this.parent().getChildContainer(true), k);
12628             
12629             
12630             
12631         }
12632         
12633         if(Roo.isIOS && this.useNativeIOS){
12634             this.initIOSView();
12635             return;
12636         }
12637         
12638         /*
12639          * Touch Devices
12640          */
12641         
12642         if(Roo.isTouch && this.mobileTouchView){
12643             this.initTouchView();
12644             return;
12645         }
12646         
12647         if(this.tickable){
12648             this.initTickableEvents();
12649             return;
12650         }
12651         
12652         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12653         
12654         if(this.hiddenName){
12655             
12656             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12657             
12658             this.hiddenField.dom.value =
12659                 this.hiddenValue !== undefined ? this.hiddenValue :
12660                 this.value !== undefined ? this.value : '';
12661
12662             // prevent input submission
12663             this.el.dom.removeAttribute('name');
12664             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12665              
12666              
12667         }
12668         //if(Roo.isGecko){
12669         //    this.el.dom.setAttribute('autocomplete', 'off');
12670         //}
12671         
12672         var cls = 'x-combo-list';
12673         
12674         //this.list = new Roo.Layer({
12675         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12676         //});
12677         
12678         var _this = this;
12679         
12680         (function(){
12681             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12682             _this.list.setWidth(lw);
12683         }).defer(100);
12684         
12685         this.list.on('mouseover', this.onViewOver, this);
12686         this.list.on('mousemove', this.onViewMove, this);
12687         
12688         this.list.on('scroll', this.onViewScroll, this);
12689         
12690         /*
12691         this.list.swallowEvent('mousewheel');
12692         this.assetHeight = 0;
12693
12694         if(this.title){
12695             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12696             this.assetHeight += this.header.getHeight();
12697         }
12698
12699         this.innerList = this.list.createChild({cls:cls+'-inner'});
12700         this.innerList.on('mouseover', this.onViewOver, this);
12701         this.innerList.on('mousemove', this.onViewMove, this);
12702         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12703         
12704         if(this.allowBlank && !this.pageSize && !this.disableClear){
12705             this.footer = this.list.createChild({cls:cls+'-ft'});
12706             this.pageTb = new Roo.Toolbar(this.footer);
12707            
12708         }
12709         if(this.pageSize){
12710             this.footer = this.list.createChild({cls:cls+'-ft'});
12711             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12712                     {pageSize: this.pageSize});
12713             
12714         }
12715         
12716         if (this.pageTb && this.allowBlank && !this.disableClear) {
12717             var _this = this;
12718             this.pageTb.add(new Roo.Toolbar.Fill(), {
12719                 cls: 'x-btn-icon x-btn-clear',
12720                 text: '&#160;',
12721                 handler: function()
12722                 {
12723                     _this.collapse();
12724                     _this.clearValue();
12725                     _this.onSelect(false, -1);
12726                 }
12727             });
12728         }
12729         if (this.footer) {
12730             this.assetHeight += this.footer.getHeight();
12731         }
12732         */
12733             
12734         if(!this.tpl){
12735             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12736         }
12737
12738         this.view = new Roo.View(this.list, this.tpl, {
12739             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12740         });
12741         //this.view.wrapEl.setDisplayed(false);
12742         this.view.on('click', this.onViewClick, this);
12743         
12744         
12745         
12746         this.store.on('beforeload', this.onBeforeLoad, this);
12747         this.store.on('load', this.onLoad, this);
12748         this.store.on('loadexception', this.onLoadException, this);
12749         /*
12750         if(this.resizable){
12751             this.resizer = new Roo.Resizable(this.list,  {
12752                pinned:true, handles:'se'
12753             });
12754             this.resizer.on('resize', function(r, w, h){
12755                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12756                 this.listWidth = w;
12757                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12758                 this.restrictHeight();
12759             }, this);
12760             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12761         }
12762         */
12763         if(!this.editable){
12764             this.editable = true;
12765             this.setEditable(false);
12766         }
12767         
12768         /*
12769         
12770         if (typeof(this.events.add.listeners) != 'undefined') {
12771             
12772             this.addicon = this.wrap.createChild(
12773                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12774        
12775             this.addicon.on('click', function(e) {
12776                 this.fireEvent('add', this);
12777             }, this);
12778         }
12779         if (typeof(this.events.edit.listeners) != 'undefined') {
12780             
12781             this.editicon = this.wrap.createChild(
12782                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12783             if (this.addicon) {
12784                 this.editicon.setStyle('margin-left', '40px');
12785             }
12786             this.editicon.on('click', function(e) {
12787                 
12788                 // we fire even  if inothing is selected..
12789                 this.fireEvent('edit', this, this.lastData );
12790                 
12791             }, this);
12792         }
12793         */
12794         
12795         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12796             "up" : function(e){
12797                 this.inKeyMode = true;
12798                 this.selectPrev();
12799             },
12800
12801             "down" : function(e){
12802                 if(!this.isExpanded()){
12803                     this.onTriggerClick();
12804                 }else{
12805                     this.inKeyMode = true;
12806                     this.selectNext();
12807                 }
12808             },
12809
12810             "enter" : function(e){
12811 //                this.onViewClick();
12812                 //return true;
12813                 this.collapse();
12814                 
12815                 if(this.fireEvent("specialkey", this, e)){
12816                     this.onViewClick(false);
12817                 }
12818                 
12819                 return true;
12820             },
12821
12822             "esc" : function(e){
12823                 this.collapse();
12824             },
12825
12826             "tab" : function(e){
12827                 this.collapse();
12828                 
12829                 if(this.fireEvent("specialkey", this, e)){
12830                     this.onViewClick(false);
12831                 }
12832                 
12833                 return true;
12834             },
12835
12836             scope : this,
12837
12838             doRelay : function(foo, bar, hname){
12839                 if(hname == 'down' || this.scope.isExpanded()){
12840                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12841                 }
12842                 return true;
12843             },
12844
12845             forceKeyDown: true
12846         });
12847         
12848         
12849         this.queryDelay = Math.max(this.queryDelay || 10,
12850                 this.mode == 'local' ? 10 : 250);
12851         
12852         
12853         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12854         
12855         if(this.typeAhead){
12856             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12857         }
12858         if(this.editable !== false){
12859             this.inputEl().on("keyup", this.onKeyUp, this);
12860         }
12861         if(this.forceSelection){
12862             this.inputEl().on('blur', this.doForce, this);
12863         }
12864         
12865         if(this.multiple){
12866             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12867             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12868         }
12869     },
12870     
12871     initTickableEvents: function()
12872     {   
12873         this.createList();
12874         
12875         if(this.hiddenName){
12876             
12877             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12878             
12879             this.hiddenField.dom.value =
12880                 this.hiddenValue !== undefined ? this.hiddenValue :
12881                 this.value !== undefined ? this.value : '';
12882
12883             // prevent input submission
12884             this.el.dom.removeAttribute('name');
12885             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12886              
12887              
12888         }
12889         
12890 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12891         
12892         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12893         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12894         if(this.triggerList){
12895             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12896         }
12897          
12898         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12899         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12900         
12901         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12902         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12903         
12904         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12905         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12906         
12907         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12908         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12909         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12910         
12911         this.okBtn.hide();
12912         this.cancelBtn.hide();
12913         
12914         var _this = this;
12915         
12916         (function(){
12917             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12918             _this.list.setWidth(lw);
12919         }).defer(100);
12920         
12921         this.list.on('mouseover', this.onViewOver, this);
12922         this.list.on('mousemove', this.onViewMove, this);
12923         
12924         this.list.on('scroll', this.onViewScroll, this);
12925         
12926         if(!this.tpl){
12927             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>';
12928         }
12929
12930         this.view = new Roo.View(this.list, this.tpl, {
12931             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12932         });
12933         
12934         //this.view.wrapEl.setDisplayed(false);
12935         this.view.on('click', this.onViewClick, this);
12936         
12937         
12938         
12939         this.store.on('beforeload', this.onBeforeLoad, this);
12940         this.store.on('load', this.onLoad, this);
12941         this.store.on('loadexception', this.onLoadException, this);
12942         
12943         if(this.editable){
12944             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12945                 "up" : function(e){
12946                     this.inKeyMode = true;
12947                     this.selectPrev();
12948                 },
12949
12950                 "down" : function(e){
12951                     this.inKeyMode = true;
12952                     this.selectNext();
12953                 },
12954
12955                 "enter" : function(e){
12956                     if(this.fireEvent("specialkey", this, e)){
12957                         this.onViewClick(false);
12958                     }
12959                     
12960                     return true;
12961                 },
12962
12963                 "esc" : function(e){
12964                     this.onTickableFooterButtonClick(e, false, false);
12965                 },
12966
12967                 "tab" : function(e){
12968                     this.fireEvent("specialkey", this, e);
12969                     
12970                     this.onTickableFooterButtonClick(e, false, false);
12971                     
12972                     return true;
12973                 },
12974
12975                 scope : this,
12976
12977                 doRelay : function(e, fn, key){
12978                     if(this.scope.isExpanded()){
12979                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12980                     }
12981                     return true;
12982                 },
12983
12984                 forceKeyDown: true
12985             });
12986         }
12987         
12988         this.queryDelay = Math.max(this.queryDelay || 10,
12989                 this.mode == 'local' ? 10 : 250);
12990         
12991         
12992         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12993         
12994         if(this.typeAhead){
12995             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12996         }
12997         
12998         if(this.editable !== false){
12999             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13000         }
13001         
13002     },
13003
13004     onDestroy : function(){
13005         if(this.view){
13006             this.view.setStore(null);
13007             this.view.el.removeAllListeners();
13008             this.view.el.remove();
13009             this.view.purgeListeners();
13010         }
13011         if(this.list){
13012             this.list.dom.innerHTML  = '';
13013         }
13014         
13015         if(this.store){
13016             this.store.un('beforeload', this.onBeforeLoad, this);
13017             this.store.un('load', this.onLoad, this);
13018             this.store.un('loadexception', this.onLoadException, this);
13019         }
13020         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13021     },
13022
13023     // private
13024     fireKey : function(e){
13025         if(e.isNavKeyPress() && !this.list.isVisible()){
13026             this.fireEvent("specialkey", this, e);
13027         }
13028     },
13029
13030     // private
13031     onResize: function(w, h){
13032 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13033 //        
13034 //        if(typeof w != 'number'){
13035 //            // we do not handle it!?!?
13036 //            return;
13037 //        }
13038 //        var tw = this.trigger.getWidth();
13039 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13040 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13041 //        var x = w - tw;
13042 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13043 //            
13044 //        //this.trigger.setStyle('left', x+'px');
13045 //        
13046 //        if(this.list && this.listWidth === undefined){
13047 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13048 //            this.list.setWidth(lw);
13049 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13050 //        }
13051         
13052     
13053         
13054     },
13055
13056     /**
13057      * Allow or prevent the user from directly editing the field text.  If false is passed,
13058      * the user will only be able to select from the items defined in the dropdown list.  This method
13059      * is the runtime equivalent of setting the 'editable' config option at config time.
13060      * @param {Boolean} value True to allow the user to directly edit the field text
13061      */
13062     setEditable : function(value){
13063         if(value == this.editable){
13064             return;
13065         }
13066         this.editable = value;
13067         if(!value){
13068             this.inputEl().dom.setAttribute('readOnly', true);
13069             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13070             this.inputEl().addClass('x-combo-noedit');
13071         }else{
13072             this.inputEl().dom.setAttribute('readOnly', false);
13073             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13074             this.inputEl().removeClass('x-combo-noedit');
13075         }
13076     },
13077
13078     // private
13079     
13080     onBeforeLoad : function(combo,opts){
13081         if(!this.hasFocus){
13082             return;
13083         }
13084          if (!opts.add) {
13085             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13086          }
13087         this.restrictHeight();
13088         this.selectedIndex = -1;
13089     },
13090
13091     // private
13092     onLoad : function(){
13093         
13094         this.hasQuery = false;
13095         
13096         if(!this.hasFocus){
13097             return;
13098         }
13099         
13100         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13101             this.loading.hide();
13102         }
13103              
13104         if(this.store.getCount() > 0){
13105             this.expand();
13106             this.restrictHeight();
13107             if(this.lastQuery == this.allQuery){
13108                 if(this.editable && !this.tickable){
13109                     this.inputEl().dom.select();
13110                 }
13111                 
13112                 if(
13113                     !this.selectByValue(this.value, true) &&
13114                     this.autoFocus && 
13115                     (
13116                         !this.store.lastOptions ||
13117                         typeof(this.store.lastOptions.add) == 'undefined' || 
13118                         this.store.lastOptions.add != true
13119                     )
13120                 ){
13121                     this.select(0, true);
13122                 }
13123             }else{
13124                 if(this.autoFocus){
13125                     this.selectNext();
13126                 }
13127                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13128                     this.taTask.delay(this.typeAheadDelay);
13129                 }
13130             }
13131         }else{
13132             this.onEmptyResults();
13133         }
13134         
13135         //this.el.focus();
13136     },
13137     // private
13138     onLoadException : function()
13139     {
13140         this.hasQuery = false;
13141         
13142         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13143             this.loading.hide();
13144         }
13145         
13146         if(this.tickable && this.editable){
13147             return;
13148         }
13149         
13150         this.collapse();
13151         // only causes errors at present
13152         //Roo.log(this.store.reader.jsonData);
13153         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13154             // fixme
13155             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13156         //}
13157         
13158         
13159     },
13160     // private
13161     onTypeAhead : function(){
13162         if(this.store.getCount() > 0){
13163             var r = this.store.getAt(0);
13164             var newValue = r.data[this.displayField];
13165             var len = newValue.length;
13166             var selStart = this.getRawValue().length;
13167             
13168             if(selStart != len){
13169                 this.setRawValue(newValue);
13170                 this.selectText(selStart, newValue.length);
13171             }
13172         }
13173     },
13174
13175     // private
13176     onSelect : function(record, index){
13177         
13178         if(this.fireEvent('beforeselect', this, record, index) !== false){
13179         
13180             this.setFromData(index > -1 ? record.data : false);
13181             
13182             this.collapse();
13183             this.fireEvent('select', this, record, index);
13184         }
13185     },
13186
13187     /**
13188      * Returns the currently selected field value or empty string if no value is set.
13189      * @return {String} value The selected value
13190      */
13191     getValue : function()
13192     {
13193         if(Roo.isIOS && this.useNativeIOS){
13194             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13195         }
13196         
13197         if(this.multiple){
13198             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13199         }
13200         
13201         if(this.valueField){
13202             return typeof this.value != 'undefined' ? this.value : '';
13203         }else{
13204             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13205         }
13206     },
13207     
13208     getRawValue : function()
13209     {
13210         if(Roo.isIOS && this.useNativeIOS){
13211             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13212         }
13213         
13214         var v = this.inputEl().getValue();
13215         
13216         return v;
13217     },
13218
13219     /**
13220      * Clears any text/value currently set in the field
13221      */
13222     clearValue : function(){
13223         
13224         if(this.hiddenField){
13225             this.hiddenField.dom.value = '';
13226         }
13227         this.value = '';
13228         this.setRawValue('');
13229         this.lastSelectionText = '';
13230         this.lastData = false;
13231         
13232         var close = this.closeTriggerEl();
13233         
13234         if(close){
13235             close.hide();
13236         }
13237         
13238         this.validate();
13239         
13240     },
13241
13242     /**
13243      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13244      * will be displayed in the field.  If the value does not match the data value of an existing item,
13245      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13246      * Otherwise the field will be blank (although the value will still be set).
13247      * @param {String} value The value to match
13248      */
13249     setValue : function(v)
13250     {
13251         if(Roo.isIOS && this.useNativeIOS){
13252             this.setIOSValue(v);
13253             return;
13254         }
13255         
13256         if(this.multiple){
13257             this.syncValue();
13258             return;
13259         }
13260         
13261         var text = v;
13262         if(this.valueField){
13263             var r = this.findRecord(this.valueField, v);
13264             if(r){
13265                 text = r.data[this.displayField];
13266             }else if(this.valueNotFoundText !== undefined){
13267                 text = this.valueNotFoundText;
13268             }
13269         }
13270         this.lastSelectionText = text;
13271         if(this.hiddenField){
13272             this.hiddenField.dom.value = v;
13273         }
13274         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13275         this.value = v;
13276         
13277         var close = this.closeTriggerEl();
13278         
13279         if(close){
13280             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13281         }
13282         
13283         this.validate();
13284     },
13285     /**
13286      * @property {Object} the last set data for the element
13287      */
13288     
13289     lastData : false,
13290     /**
13291      * Sets the value of the field based on a object which is related to the record format for the store.
13292      * @param {Object} value the value to set as. or false on reset?
13293      */
13294     setFromData : function(o){
13295         
13296         if(this.multiple){
13297             this.addItem(o);
13298             return;
13299         }
13300             
13301         var dv = ''; // display value
13302         var vv = ''; // value value..
13303         this.lastData = o;
13304         if (this.displayField) {
13305             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13306         } else {
13307             // this is an error condition!!!
13308             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13309         }
13310         
13311         if(this.valueField){
13312             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13313         }
13314         
13315         var close = this.closeTriggerEl();
13316         
13317         if(close){
13318             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13319         }
13320         
13321         if(this.hiddenField){
13322             this.hiddenField.dom.value = vv;
13323             
13324             this.lastSelectionText = dv;
13325             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13326             this.value = vv;
13327             return;
13328         }
13329         // no hidden field.. - we store the value in 'value', but still display
13330         // display field!!!!
13331         this.lastSelectionText = dv;
13332         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13333         this.value = vv;
13334         
13335         
13336         
13337     },
13338     // private
13339     reset : function(){
13340         // overridden so that last data is reset..
13341         
13342         if(this.multiple){
13343             this.clearItem();
13344             return;
13345         }
13346         
13347         this.setValue(this.originalValue);
13348         //this.clearInvalid();
13349         this.lastData = false;
13350         if (this.view) {
13351             this.view.clearSelections();
13352         }
13353         
13354         this.validate();
13355     },
13356     // private
13357     findRecord : function(prop, value){
13358         var record;
13359         if(this.store.getCount() > 0){
13360             this.store.each(function(r){
13361                 if(r.data[prop] == value){
13362                     record = r;
13363                     return false;
13364                 }
13365                 return true;
13366             });
13367         }
13368         return record;
13369     },
13370     
13371     getName: function()
13372     {
13373         // returns hidden if it's set..
13374         if (!this.rendered) {return ''};
13375         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13376         
13377     },
13378     // private
13379     onViewMove : function(e, t){
13380         this.inKeyMode = false;
13381     },
13382
13383     // private
13384     onViewOver : function(e, t){
13385         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13386             return;
13387         }
13388         var item = this.view.findItemFromChild(t);
13389         
13390         if(item){
13391             var index = this.view.indexOf(item);
13392             this.select(index, false);
13393         }
13394     },
13395
13396     // private
13397     onViewClick : function(view, doFocus, el, e)
13398     {
13399         var index = this.view.getSelectedIndexes()[0];
13400         
13401         var r = this.store.getAt(index);
13402         
13403         if(this.tickable){
13404             
13405             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13406                 return;
13407             }
13408             
13409             var rm = false;
13410             var _this = this;
13411             
13412             Roo.each(this.tickItems, function(v,k){
13413                 
13414                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13415                     Roo.log(v);
13416                     _this.tickItems.splice(k, 1);
13417                     
13418                     if(typeof(e) == 'undefined' && view == false){
13419                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13420                     }
13421                     
13422                     rm = true;
13423                     return;
13424                 }
13425             });
13426             
13427             if(rm){
13428                 return;
13429             }
13430             
13431             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13432                 this.tickItems.push(r.data);
13433             }
13434             
13435             if(typeof(e) == 'undefined' && view == false){
13436                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13437             }
13438                     
13439             return;
13440         }
13441         
13442         if(r){
13443             this.onSelect(r, index);
13444         }
13445         if(doFocus !== false && !this.blockFocus){
13446             this.inputEl().focus();
13447         }
13448     },
13449
13450     // private
13451     restrictHeight : function(){
13452         //this.innerList.dom.style.height = '';
13453         //var inner = this.innerList.dom;
13454         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13455         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13456         //this.list.beginUpdate();
13457         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13458         this.list.alignTo(this.inputEl(), this.listAlign);
13459         this.list.alignTo(this.inputEl(), this.listAlign);
13460         //this.list.endUpdate();
13461     },
13462
13463     // private
13464     onEmptyResults : function(){
13465         
13466         if(this.tickable && this.editable){
13467             this.restrictHeight();
13468             return;
13469         }
13470         
13471         this.collapse();
13472     },
13473
13474     /**
13475      * Returns true if the dropdown list is expanded, else false.
13476      */
13477     isExpanded : function(){
13478         return this.list.isVisible();
13479     },
13480
13481     /**
13482      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13483      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13484      * @param {String} value The data value of the item to select
13485      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13486      * selected item if it is not currently in view (defaults to true)
13487      * @return {Boolean} True if the value matched an item in the list, else false
13488      */
13489     selectByValue : function(v, scrollIntoView){
13490         if(v !== undefined && v !== null){
13491             var r = this.findRecord(this.valueField || this.displayField, v);
13492             if(r){
13493                 this.select(this.store.indexOf(r), scrollIntoView);
13494                 return true;
13495             }
13496         }
13497         return false;
13498     },
13499
13500     /**
13501      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13502      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13503      * @param {Number} index The zero-based index of the list item to select
13504      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13505      * selected item if it is not currently in view (defaults to true)
13506      */
13507     select : function(index, scrollIntoView){
13508         this.selectedIndex = index;
13509         this.view.select(index);
13510         if(scrollIntoView !== false){
13511             var el = this.view.getNode(index);
13512             /*
13513              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13514              */
13515             if(el){
13516                 this.list.scrollChildIntoView(el, false);
13517             }
13518         }
13519     },
13520
13521     // private
13522     selectNext : function(){
13523         var ct = this.store.getCount();
13524         if(ct > 0){
13525             if(this.selectedIndex == -1){
13526                 this.select(0);
13527             }else if(this.selectedIndex < ct-1){
13528                 this.select(this.selectedIndex+1);
13529             }
13530         }
13531     },
13532
13533     // private
13534     selectPrev : function(){
13535         var ct = this.store.getCount();
13536         if(ct > 0){
13537             if(this.selectedIndex == -1){
13538                 this.select(0);
13539             }else if(this.selectedIndex != 0){
13540                 this.select(this.selectedIndex-1);
13541             }
13542         }
13543     },
13544
13545     // private
13546     onKeyUp : function(e){
13547         if(this.editable !== false && !e.isSpecialKey()){
13548             this.lastKey = e.getKey();
13549             this.dqTask.delay(this.queryDelay);
13550         }
13551     },
13552
13553     // private
13554     validateBlur : function(){
13555         return !this.list || !this.list.isVisible();   
13556     },
13557
13558     // private
13559     initQuery : function(){
13560         
13561         var v = this.getRawValue();
13562         
13563         if(this.tickable && this.editable){
13564             v = this.tickableInputEl().getValue();
13565         }
13566         
13567         this.doQuery(v);
13568     },
13569
13570     // private
13571     doForce : function(){
13572         if(this.inputEl().dom.value.length > 0){
13573             this.inputEl().dom.value =
13574                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13575              
13576         }
13577     },
13578
13579     /**
13580      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13581      * query allowing the query action to be canceled if needed.
13582      * @param {String} query The SQL query to execute
13583      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13584      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13585      * saved in the current store (defaults to false)
13586      */
13587     doQuery : function(q, forceAll){
13588         
13589         if(q === undefined || q === null){
13590             q = '';
13591         }
13592         var qe = {
13593             query: q,
13594             forceAll: forceAll,
13595             combo: this,
13596             cancel:false
13597         };
13598         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13599             return false;
13600         }
13601         q = qe.query;
13602         
13603         forceAll = qe.forceAll;
13604         if(forceAll === true || (q.length >= this.minChars)){
13605             
13606             this.hasQuery = true;
13607             
13608             if(this.lastQuery != q || this.alwaysQuery){
13609                 this.lastQuery = q;
13610                 if(this.mode == 'local'){
13611                     this.selectedIndex = -1;
13612                     if(forceAll){
13613                         this.store.clearFilter();
13614                     }else{
13615                         
13616                         if(this.specialFilter){
13617                             this.fireEvent('specialfilter', this);
13618                             this.onLoad();
13619                             return;
13620                         }
13621                         
13622                         this.store.filter(this.displayField, q);
13623                     }
13624                     
13625                     this.store.fireEvent("datachanged", this.store);
13626                     
13627                     this.onLoad();
13628                     
13629                     
13630                 }else{
13631                     
13632                     this.store.baseParams[this.queryParam] = q;
13633                     
13634                     var options = {params : this.getParams(q)};
13635                     
13636                     if(this.loadNext){
13637                         options.add = true;
13638                         options.params.start = this.page * this.pageSize;
13639                     }
13640                     
13641                     this.store.load(options);
13642                     
13643                     /*
13644                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13645                      *  we should expand the list on onLoad
13646                      *  so command out it
13647                      */
13648 //                    this.expand();
13649                 }
13650             }else{
13651                 this.selectedIndex = -1;
13652                 this.onLoad();   
13653             }
13654         }
13655         
13656         this.loadNext = false;
13657     },
13658     
13659     // private
13660     getParams : function(q){
13661         var p = {};
13662         //p[this.queryParam] = q;
13663         
13664         if(this.pageSize){
13665             p.start = 0;
13666             p.limit = this.pageSize;
13667         }
13668         return p;
13669     },
13670
13671     /**
13672      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13673      */
13674     collapse : function(){
13675         if(!this.isExpanded()){
13676             return;
13677         }
13678         
13679         this.list.hide();
13680         
13681         this.hasFocus = false;
13682         
13683         if(this.tickable){
13684             this.okBtn.hide();
13685             this.cancelBtn.hide();
13686             this.trigger.show();
13687             
13688             if(this.editable){
13689                 this.tickableInputEl().dom.value = '';
13690                 this.tickableInputEl().blur();
13691             }
13692             
13693         }
13694         
13695         Roo.get(document).un('mousedown', this.collapseIf, this);
13696         Roo.get(document).un('mousewheel', this.collapseIf, this);
13697         if (!this.editable) {
13698             Roo.get(document).un('keydown', this.listKeyPress, this);
13699         }
13700         this.fireEvent('collapse', this);
13701         
13702         this.validate();
13703     },
13704
13705     // private
13706     collapseIf : function(e){
13707         var in_combo  = e.within(this.el);
13708         var in_list =  e.within(this.list);
13709         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13710         
13711         if (in_combo || in_list || is_list) {
13712             //e.stopPropagation();
13713             return;
13714         }
13715         
13716         if(this.tickable){
13717             this.onTickableFooterButtonClick(e, false, false);
13718         }
13719
13720         this.collapse();
13721         
13722     },
13723
13724     /**
13725      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13726      */
13727     expand : function(){
13728        
13729         if(this.isExpanded() || !this.hasFocus){
13730             return;
13731         }
13732         
13733         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13734         this.list.setWidth(lw);
13735         
13736         Roo.log('expand');
13737         
13738         this.list.show();
13739         
13740         this.restrictHeight();
13741         
13742         if(this.tickable){
13743             
13744             this.tickItems = Roo.apply([], this.item);
13745             
13746             this.okBtn.show();
13747             this.cancelBtn.show();
13748             this.trigger.hide();
13749             
13750             if(this.editable){
13751                 this.tickableInputEl().focus();
13752             }
13753             
13754         }
13755         
13756         Roo.get(document).on('mousedown', this.collapseIf, this);
13757         Roo.get(document).on('mousewheel', this.collapseIf, this);
13758         if (!this.editable) {
13759             Roo.get(document).on('keydown', this.listKeyPress, this);
13760         }
13761         
13762         this.fireEvent('expand', this);
13763     },
13764
13765     // private
13766     // Implements the default empty TriggerField.onTriggerClick function
13767     onTriggerClick : function(e)
13768     {
13769         Roo.log('trigger click');
13770         
13771         if(this.disabled || !this.triggerList){
13772             return;
13773         }
13774         
13775         this.page = 0;
13776         this.loadNext = false;
13777         
13778         if(this.isExpanded()){
13779             this.collapse();
13780             if (!this.blockFocus) {
13781                 this.inputEl().focus();
13782             }
13783             
13784         }else {
13785             this.hasFocus = true;
13786             if(this.triggerAction == 'all') {
13787                 this.doQuery(this.allQuery, true);
13788             } else {
13789                 this.doQuery(this.getRawValue());
13790             }
13791             if (!this.blockFocus) {
13792                 this.inputEl().focus();
13793             }
13794         }
13795     },
13796     
13797     onTickableTriggerClick : function(e)
13798     {
13799         if(this.disabled){
13800             return;
13801         }
13802         
13803         this.page = 0;
13804         this.loadNext = false;
13805         this.hasFocus = true;
13806         
13807         if(this.triggerAction == 'all') {
13808             this.doQuery(this.allQuery, true);
13809         } else {
13810             this.doQuery(this.getRawValue());
13811         }
13812     },
13813     
13814     onSearchFieldClick : function(e)
13815     {
13816         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13817             this.onTickableFooterButtonClick(e, false, false);
13818             return;
13819         }
13820         
13821         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13822             return;
13823         }
13824         
13825         this.page = 0;
13826         this.loadNext = false;
13827         this.hasFocus = true;
13828         
13829         if(this.triggerAction == 'all') {
13830             this.doQuery(this.allQuery, true);
13831         } else {
13832             this.doQuery(this.getRawValue());
13833         }
13834     },
13835     
13836     listKeyPress : function(e)
13837     {
13838         //Roo.log('listkeypress');
13839         // scroll to first matching element based on key pres..
13840         if (e.isSpecialKey()) {
13841             return false;
13842         }
13843         var k = String.fromCharCode(e.getKey()).toUpperCase();
13844         //Roo.log(k);
13845         var match  = false;
13846         var csel = this.view.getSelectedNodes();
13847         var cselitem = false;
13848         if (csel.length) {
13849             var ix = this.view.indexOf(csel[0]);
13850             cselitem  = this.store.getAt(ix);
13851             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13852                 cselitem = false;
13853             }
13854             
13855         }
13856         
13857         this.store.each(function(v) { 
13858             if (cselitem) {
13859                 // start at existing selection.
13860                 if (cselitem.id == v.id) {
13861                     cselitem = false;
13862                 }
13863                 return true;
13864             }
13865                 
13866             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13867                 match = this.store.indexOf(v);
13868                 return false;
13869             }
13870             return true;
13871         }, this);
13872         
13873         if (match === false) {
13874             return true; // no more action?
13875         }
13876         // scroll to?
13877         this.view.select(match);
13878         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13879         sn.scrollIntoView(sn.dom.parentNode, false);
13880     },
13881     
13882     onViewScroll : function(e, t){
13883         
13884         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){
13885             return;
13886         }
13887         
13888         this.hasQuery = true;
13889         
13890         this.loading = this.list.select('.loading', true).first();
13891         
13892         if(this.loading === null){
13893             this.list.createChild({
13894                 tag: 'div',
13895                 cls: 'loading roo-select2-more-results roo-select2-active',
13896                 html: 'Loading more results...'
13897             });
13898             
13899             this.loading = this.list.select('.loading', true).first();
13900             
13901             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13902             
13903             this.loading.hide();
13904         }
13905         
13906         this.loading.show();
13907         
13908         var _combo = this;
13909         
13910         this.page++;
13911         this.loadNext = true;
13912         
13913         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13914         
13915         return;
13916     },
13917     
13918     addItem : function(o)
13919     {   
13920         var dv = ''; // display value
13921         
13922         if (this.displayField) {
13923             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13924         } else {
13925             // this is an error condition!!!
13926             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13927         }
13928         
13929         if(!dv.length){
13930             return;
13931         }
13932         
13933         var choice = this.choices.createChild({
13934             tag: 'li',
13935             cls: 'roo-select2-search-choice',
13936             cn: [
13937                 {
13938                     tag: 'div',
13939                     html: dv
13940                 },
13941                 {
13942                     tag: 'a',
13943                     href: '#',
13944                     cls: 'roo-select2-search-choice-close',
13945                     tabindex: '-1'
13946                 }
13947             ]
13948             
13949         }, this.searchField);
13950         
13951         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13952         
13953         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13954         
13955         this.item.push(o);
13956         
13957         this.lastData = o;
13958         
13959         this.syncValue();
13960         
13961         this.inputEl().dom.value = '';
13962         
13963         this.validate();
13964     },
13965     
13966     onRemoveItem : function(e, _self, o)
13967     {
13968         e.preventDefault();
13969         
13970         this.lastItem = Roo.apply([], this.item);
13971         
13972         var index = this.item.indexOf(o.data) * 1;
13973         
13974         if( index < 0){
13975             Roo.log('not this item?!');
13976             return;
13977         }
13978         
13979         this.item.splice(index, 1);
13980         o.item.remove();
13981         
13982         this.syncValue();
13983         
13984         this.fireEvent('remove', this, e);
13985         
13986         this.validate();
13987         
13988     },
13989     
13990     syncValue : function()
13991     {
13992         if(!this.item.length){
13993             this.clearValue();
13994             return;
13995         }
13996             
13997         var value = [];
13998         var _this = this;
13999         Roo.each(this.item, function(i){
14000             if(_this.valueField){
14001                 value.push(i[_this.valueField]);
14002                 return;
14003             }
14004
14005             value.push(i);
14006         });
14007
14008         this.value = value.join(',');
14009
14010         if(this.hiddenField){
14011             this.hiddenField.dom.value = this.value;
14012         }
14013         
14014         this.store.fireEvent("datachanged", this.store);
14015         
14016         this.validate();
14017     },
14018     
14019     clearItem : function()
14020     {
14021         if(!this.multiple){
14022             return;
14023         }
14024         
14025         this.item = [];
14026         
14027         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14028            c.remove();
14029         });
14030         
14031         this.syncValue();
14032         
14033         this.validate();
14034         
14035         if(this.tickable && !Roo.isTouch){
14036             this.view.refresh();
14037         }
14038     },
14039     
14040     inputEl: function ()
14041     {
14042         if(Roo.isIOS && this.useNativeIOS){
14043             return this.el.select('select.roo-ios-select', true).first();
14044         }
14045         
14046         if(Roo.isTouch && this.mobileTouchView){
14047             return this.el.select('input.form-control',true).first();
14048         }
14049         
14050         if(this.tickable){
14051             return this.searchField;
14052         }
14053         
14054         return this.el.select('input.form-control',true).first();
14055     },
14056     
14057     onTickableFooterButtonClick : function(e, btn, el)
14058     {
14059         e.preventDefault();
14060         
14061         this.lastItem = Roo.apply([], this.item);
14062         
14063         if(btn && btn.name == 'cancel'){
14064             this.tickItems = Roo.apply([], this.item);
14065             this.collapse();
14066             return;
14067         }
14068         
14069         this.clearItem();
14070         
14071         var _this = this;
14072         
14073         Roo.each(this.tickItems, function(o){
14074             _this.addItem(o);
14075         });
14076         
14077         this.collapse();
14078         
14079     },
14080     
14081     validate : function()
14082     {
14083         var v = this.getRawValue();
14084         
14085         if(this.multiple){
14086             v = this.getValue();
14087         }
14088         
14089         if(this.disabled || this.allowBlank || v.length){
14090             this.markValid();
14091             return true;
14092         }
14093         
14094         this.markInvalid();
14095         return false;
14096     },
14097     
14098     tickableInputEl : function()
14099     {
14100         if(!this.tickable || !this.editable){
14101             return this.inputEl();
14102         }
14103         
14104         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14105     },
14106     
14107     
14108     getAutoCreateTouchView : function()
14109     {
14110         var id = Roo.id();
14111         
14112         var cfg = {
14113             cls: 'form-group' //input-group
14114         };
14115         
14116         var input =  {
14117             tag: 'input',
14118             id : id,
14119             type : this.inputType,
14120             cls : 'form-control x-combo-noedit',
14121             autocomplete: 'new-password',
14122             placeholder : this.placeholder || '',
14123             readonly : true
14124         };
14125         
14126         if (this.name) {
14127             input.name = this.name;
14128         }
14129         
14130         if (this.size) {
14131             input.cls += ' input-' + this.size;
14132         }
14133         
14134         if (this.disabled) {
14135             input.disabled = true;
14136         }
14137         
14138         var inputblock = {
14139             cls : '',
14140             cn : [
14141                 input
14142             ]
14143         };
14144         
14145         if(this.before){
14146             inputblock.cls += ' input-group';
14147             
14148             inputblock.cn.unshift({
14149                 tag :'span',
14150                 cls : 'input-group-addon',
14151                 html : this.before
14152             });
14153         }
14154         
14155         if(this.removable && !this.multiple){
14156             inputblock.cls += ' roo-removable';
14157             
14158             inputblock.cn.push({
14159                 tag: 'button',
14160                 html : 'x',
14161                 cls : 'roo-combo-removable-btn close'
14162             });
14163         }
14164
14165         if(this.hasFeedback && !this.allowBlank){
14166             
14167             inputblock.cls += ' has-feedback';
14168             
14169             inputblock.cn.push({
14170                 tag: 'span',
14171                 cls: 'glyphicon form-control-feedback'
14172             });
14173             
14174         }
14175         
14176         if (this.after) {
14177             
14178             inputblock.cls += (this.before) ? '' : ' input-group';
14179             
14180             inputblock.cn.push({
14181                 tag :'span',
14182                 cls : 'input-group-addon',
14183                 html : this.after
14184             });
14185         }
14186
14187         var box = {
14188             tag: 'div',
14189             cn: [
14190                 {
14191                     tag: 'input',
14192                     type : 'hidden',
14193                     cls: 'form-hidden-field'
14194                 },
14195                 inputblock
14196             ]
14197             
14198         };
14199         
14200         if(this.multiple){
14201             box = {
14202                 tag: 'div',
14203                 cn: [
14204                     {
14205                         tag: 'input',
14206                         type : 'hidden',
14207                         cls: 'form-hidden-field'
14208                     },
14209                     {
14210                         tag: 'ul',
14211                         cls: 'roo-select2-choices',
14212                         cn:[
14213                             {
14214                                 tag: 'li',
14215                                 cls: 'roo-select2-search-field',
14216                                 cn: [
14217
14218                                     inputblock
14219                                 ]
14220                             }
14221                         ]
14222                     }
14223                 ]
14224             }
14225         };
14226         
14227         var combobox = {
14228             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14229             cn: [
14230                 box
14231             ]
14232         };
14233         
14234         if(!this.multiple && this.showToggleBtn){
14235             
14236             var caret = {
14237                         tag: 'span',
14238                         cls: 'caret'
14239             };
14240             
14241             if (this.caret != false) {
14242                 caret = {
14243                      tag: 'i',
14244                      cls: 'fa fa-' + this.caret
14245                 };
14246                 
14247             }
14248             
14249             combobox.cn.push({
14250                 tag :'span',
14251                 cls : 'input-group-addon btn dropdown-toggle',
14252                 cn : [
14253                     caret,
14254                     {
14255                         tag: 'span',
14256                         cls: 'combobox-clear',
14257                         cn  : [
14258                             {
14259                                 tag : 'i',
14260                                 cls: 'icon-remove'
14261                             }
14262                         ]
14263                     }
14264                 ]
14265
14266             })
14267         }
14268         
14269         if(this.multiple){
14270             combobox.cls += ' roo-select2-container-multi';
14271         }
14272         
14273         var align = this.labelAlign || this.parentLabelAlign();
14274         
14275         cfg.cn = combobox;
14276         
14277         if(this.fieldLabel.length && this.labelWidth){
14278             
14279             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14280             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14281             
14282             cfg.cn = [
14283                 {
14284                    tag : 'i',
14285                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14286                    tooltip : 'This field is required'
14287                 },
14288                 {
14289                     tag: 'label',
14290                     cls : 'control-label ' + lw,
14291                     html : this.fieldLabel
14292
14293                 },
14294                 {
14295                     cls : cw, 
14296                     cn: [
14297                         combobox
14298                     ]
14299                 }
14300             ];
14301             
14302             if(this.indicatorpos == 'right'){
14303                 cfg.cn = [
14304                     {
14305                         tag: 'label',
14306                         cls : 'control-label ' + lw,
14307                         html : this.fieldLabel
14308
14309                     },
14310                     {
14311                        tag : 'i',
14312                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14313                        tooltip : 'This field is required'
14314                     },
14315                     {
14316                         cls : cw, 
14317                         cn: [
14318                             combobox
14319                         ]
14320                     }
14321                 ];
14322             }
14323         }
14324         
14325         var settings = this;
14326         
14327         ['xs','sm','md','lg'].map(function(size){
14328             if (settings[size]) {
14329                 cfg.cls += ' col-' + size + '-' + settings[size];
14330             }
14331         });
14332         
14333         return cfg;
14334     },
14335     
14336     initTouchView : function()
14337     {
14338         this.renderTouchView();
14339         
14340         this.touchViewEl.on('scroll', function(){
14341             this.el.dom.scrollTop = 0;
14342         }, this);
14343         
14344         this.originalValue = this.getValue();
14345         
14346         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14347         
14348         this.inputEl().on("click", this.showTouchView, this);
14349         if (this.triggerEl) {
14350             this.triggerEl.on("click", this.showTouchView, this);
14351         }
14352         
14353         
14354         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14355         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14356         
14357         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14358         
14359         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14360         this.store.on('load', this.onTouchViewLoad, this);
14361         this.store.on('loadexception', this.onTouchViewLoadException, this);
14362         
14363         if(this.hiddenName){
14364             
14365             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14366             
14367             this.hiddenField.dom.value =
14368                 this.hiddenValue !== undefined ? this.hiddenValue :
14369                 this.value !== undefined ? this.value : '';
14370         
14371             this.el.dom.removeAttribute('name');
14372             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14373         }
14374         
14375         if(this.multiple){
14376             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14377             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14378         }
14379         
14380         if(this.removable && !this.multiple){
14381             var close = this.closeTriggerEl();
14382             if(close){
14383                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14384                 close.on('click', this.removeBtnClick, this, close);
14385             }
14386         }
14387         /*
14388          * fix the bug in Safari iOS8
14389          */
14390         this.inputEl().on("focus", function(e){
14391             document.activeElement.blur();
14392         }, this);
14393         
14394         return;
14395         
14396         
14397     },
14398     
14399     renderTouchView : function()
14400     {
14401         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14402         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14403         
14404         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14405         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14406         
14407         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14408         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14409         this.touchViewBodyEl.setStyle('overflow', 'auto');
14410         
14411         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14412         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14413         
14414         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14415         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14416         
14417     },
14418     
14419     showTouchView : function()
14420     {
14421         if(this.disabled){
14422             return;
14423         }
14424         
14425         this.touchViewHeaderEl.hide();
14426
14427         if(this.modalTitle.length){
14428             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14429             this.touchViewHeaderEl.show();
14430         }
14431
14432         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14433         this.touchViewEl.show();
14434
14435         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14436         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14437                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14438
14439         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14440
14441         if(this.modalTitle.length){
14442             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14443         }
14444         
14445         this.touchViewBodyEl.setHeight(bodyHeight);
14446
14447         if(this.animate){
14448             var _this = this;
14449             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14450         }else{
14451             this.touchViewEl.addClass('in');
14452         }
14453
14454         this.doTouchViewQuery();
14455         
14456     },
14457     
14458     hideTouchView : function()
14459     {
14460         this.touchViewEl.removeClass('in');
14461
14462         if(this.animate){
14463             var _this = this;
14464             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14465         }else{
14466             this.touchViewEl.setStyle('display', 'none');
14467         }
14468         
14469     },
14470     
14471     setTouchViewValue : function()
14472     {
14473         if(this.multiple){
14474             this.clearItem();
14475         
14476             var _this = this;
14477
14478             Roo.each(this.tickItems, function(o){
14479                 this.addItem(o);
14480             }, this);
14481         }
14482         
14483         this.hideTouchView();
14484     },
14485     
14486     doTouchViewQuery : function()
14487     {
14488         var qe = {
14489             query: '',
14490             forceAll: true,
14491             combo: this,
14492             cancel:false
14493         };
14494         
14495         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14496             return false;
14497         }
14498         
14499         if(!this.alwaysQuery || this.mode == 'local'){
14500             this.onTouchViewLoad();
14501             return;
14502         }
14503         
14504         this.store.load();
14505     },
14506     
14507     onTouchViewBeforeLoad : function(combo,opts)
14508     {
14509         return;
14510     },
14511
14512     // private
14513     onTouchViewLoad : function()
14514     {
14515         if(this.store.getCount() < 1){
14516             this.onTouchViewEmptyResults();
14517             return;
14518         }
14519         
14520         this.clearTouchView();
14521         
14522         var rawValue = this.getRawValue();
14523         
14524         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14525         
14526         this.tickItems = [];
14527         
14528         this.store.data.each(function(d, rowIndex){
14529             var row = this.touchViewListGroup.createChild(template);
14530             
14531             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14532                 row.addClass(d.data.cls);
14533             }
14534             
14535             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14536                 var cfg = {
14537                     data : d.data,
14538                     html : d.data[this.displayField]
14539                 };
14540                 
14541                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14542                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14543                 }
14544             }
14545             row.removeClass('selected');
14546             if(!this.multiple && this.valueField &&
14547                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14548             {
14549                 // radio buttons..
14550                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14551                 row.addClass('selected');
14552             }
14553             
14554             if(this.multiple && this.valueField &&
14555                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14556             {
14557                 
14558                 // checkboxes...
14559                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14560                 this.tickItems.push(d.data);
14561             }
14562             
14563             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14564             
14565         }, this);
14566         
14567         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14568         
14569         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14570
14571         if(this.modalTitle.length){
14572             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14573         }
14574
14575         var listHeight = this.touchViewListGroup.getHeight();
14576         
14577         var _this = this;
14578         
14579         if(firstChecked && listHeight > bodyHeight){
14580             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14581         }
14582         
14583     },
14584     
14585     onTouchViewLoadException : function()
14586     {
14587         this.hideTouchView();
14588     },
14589     
14590     onTouchViewEmptyResults : function()
14591     {
14592         this.clearTouchView();
14593         
14594         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14595         
14596         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14597         
14598     },
14599     
14600     clearTouchView : function()
14601     {
14602         this.touchViewListGroup.dom.innerHTML = '';
14603     },
14604     
14605     onTouchViewClick : function(e, el, o)
14606     {
14607         e.preventDefault();
14608         
14609         var row = o.row;
14610         var rowIndex = o.rowIndex;
14611         
14612         var r = this.store.getAt(rowIndex);
14613         
14614         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14615             
14616             if(!this.multiple){
14617                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14618                     c.dom.removeAttribute('checked');
14619                 }, this);
14620
14621                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14622
14623                 this.setFromData(r.data);
14624
14625                 var close = this.closeTriggerEl();
14626
14627                 if(close){
14628                     close.show();
14629                 }
14630
14631                 this.hideTouchView();
14632
14633                 this.fireEvent('select', this, r, rowIndex);
14634
14635                 return;
14636             }
14637
14638             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14639                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14640                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14641                 return;
14642             }
14643
14644             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14645             this.addItem(r.data);
14646             this.tickItems.push(r.data);
14647         }
14648     },
14649     
14650     getAutoCreateNativeIOS : function()
14651     {
14652         var cfg = {
14653             cls: 'form-group' //input-group,
14654         };
14655         
14656         var combobox =  {
14657             tag: 'select',
14658             cls : 'roo-ios-select'
14659         };
14660         
14661         if (this.name) {
14662             combobox.name = this.name;
14663         }
14664         
14665         if (this.disabled) {
14666             combobox.disabled = true;
14667         }
14668         
14669         var settings = this;
14670         
14671         ['xs','sm','md','lg'].map(function(size){
14672             if (settings[size]) {
14673                 cfg.cls += ' col-' + size + '-' + settings[size];
14674             }
14675         });
14676         
14677         cfg.cn = combobox;
14678         
14679         return cfg;
14680         
14681     },
14682     
14683     initIOSView : function()
14684     {
14685         this.store.on('load', this.onIOSViewLoad, this);
14686         
14687         return;
14688     },
14689     
14690     onIOSViewLoad : function()
14691     {
14692         if(this.store.getCount() < 1){
14693             return;
14694         }
14695         
14696         this.clearIOSView();
14697         
14698         if(this.allowBlank) {
14699             
14700             var default_text = '-- SELECT --';
14701             
14702             var opt = this.inputEl().createChild({
14703                 tag: 'option',
14704                 value : 0,
14705                 html : default_text
14706             });
14707             
14708             var o = {};
14709             o[this.valueField] = 0;
14710             o[this.displayField] = default_text;
14711             
14712             this.ios_options.push({
14713                 data : o,
14714                 el : opt
14715             });
14716             
14717         }
14718         
14719         this.store.data.each(function(d, rowIndex){
14720             
14721             var html = '';
14722             
14723             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14724                 html = d.data[this.displayField];
14725             }
14726             
14727             var value = '';
14728             
14729             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14730                 value = d.data[this.valueField];
14731             }
14732             
14733             var option = {
14734                 tag: 'option',
14735                 value : value,
14736                 html : html
14737             };
14738             
14739             if(this.value == d.data[this.valueField]){
14740                 option['selected'] = true;
14741             }
14742             
14743             var opt = this.inputEl().createChild(option);
14744             
14745             this.ios_options.push({
14746                 data : d.data,
14747                 el : opt
14748             });
14749             
14750         }, this);
14751         
14752         this.inputEl().on('change', function(){
14753            this.fireEvent('select', this);
14754         }, this);
14755         
14756     },
14757     
14758     clearIOSView: function()
14759     {
14760         this.inputEl().dom.innerHTML = '';
14761         
14762         this.ios_options = [];
14763     },
14764     
14765     setIOSValue: function(v)
14766     {
14767         this.value = v;
14768         
14769         if(!this.ios_options){
14770             return;
14771         }
14772         
14773         Roo.each(this.ios_options, function(opts){
14774            
14775            opts.el.dom.removeAttribute('selected');
14776            
14777            if(opts.data[this.valueField] != v){
14778                return;
14779            }
14780            
14781            opts.el.dom.setAttribute('selected', true);
14782            
14783         }, this);
14784     }
14785
14786     /** 
14787     * @cfg {Boolean} grow 
14788     * @hide 
14789     */
14790     /** 
14791     * @cfg {Number} growMin 
14792     * @hide 
14793     */
14794     /** 
14795     * @cfg {Number} growMax 
14796     * @hide 
14797     */
14798     /**
14799      * @hide
14800      * @method autoSize
14801      */
14802 });
14803
14804 Roo.apply(Roo.bootstrap.ComboBox,  {
14805     
14806     header : {
14807         tag: 'div',
14808         cls: 'modal-header',
14809         cn: [
14810             {
14811                 tag: 'h4',
14812                 cls: 'modal-title'
14813             }
14814         ]
14815     },
14816     
14817     body : {
14818         tag: 'div',
14819         cls: 'modal-body',
14820         cn: [
14821             {
14822                 tag: 'ul',
14823                 cls: 'list-group'
14824             }
14825         ]
14826     },
14827     
14828     listItemRadio : {
14829         tag: 'li',
14830         cls: 'list-group-item',
14831         cn: [
14832             {
14833                 tag: 'span',
14834                 cls: 'roo-combobox-list-group-item-value'
14835             },
14836             {
14837                 tag: 'div',
14838                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14839                 cn: [
14840                     {
14841                         tag: 'input',
14842                         type: 'radio'
14843                     },
14844                     {
14845                         tag: 'label'
14846                     }
14847                 ]
14848             }
14849         ]
14850     },
14851     
14852     listItemCheckbox : {
14853         tag: 'li',
14854         cls: 'list-group-item',
14855         cn: [
14856             {
14857                 tag: 'span',
14858                 cls: 'roo-combobox-list-group-item-value'
14859             },
14860             {
14861                 tag: 'div',
14862                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14863                 cn: [
14864                     {
14865                         tag: 'input',
14866                         type: 'checkbox'
14867                     },
14868                     {
14869                         tag: 'label'
14870                     }
14871                 ]
14872             }
14873         ]
14874     },
14875     
14876     emptyResult : {
14877         tag: 'div',
14878         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14879     },
14880     
14881     footer : {
14882         tag: 'div',
14883         cls: 'modal-footer',
14884         cn: [
14885             {
14886                 tag: 'div',
14887                 cls: 'row',
14888                 cn: [
14889                     {
14890                         tag: 'div',
14891                         cls: 'col-xs-6 text-left',
14892                         cn: {
14893                             tag: 'button',
14894                             cls: 'btn btn-danger roo-touch-view-cancel',
14895                             html: 'Cancel'
14896                         }
14897                     },
14898                     {
14899                         tag: 'div',
14900                         cls: 'col-xs-6 text-right',
14901                         cn: {
14902                             tag: 'button',
14903                             cls: 'btn btn-success roo-touch-view-ok',
14904                             html: 'OK'
14905                         }
14906                     }
14907                 ]
14908             }
14909         ]
14910         
14911     }
14912 });
14913
14914 Roo.apply(Roo.bootstrap.ComboBox,  {
14915     
14916     touchViewTemplate : {
14917         tag: 'div',
14918         cls: 'modal fade roo-combobox-touch-view',
14919         cn: [
14920             {
14921                 tag: 'div',
14922                 cls: 'modal-dialog',
14923                 style : 'position:fixed', // we have to fix position....
14924                 cn: [
14925                     {
14926                         tag: 'div',
14927                         cls: 'modal-content',
14928                         cn: [
14929                             Roo.bootstrap.ComboBox.header,
14930                             Roo.bootstrap.ComboBox.body,
14931                             Roo.bootstrap.ComboBox.footer
14932                         ]
14933                     }
14934                 ]
14935             }
14936         ]
14937     }
14938 });/*
14939  * Based on:
14940  * Ext JS Library 1.1.1
14941  * Copyright(c) 2006-2007, Ext JS, LLC.
14942  *
14943  * Originally Released Under LGPL - original licence link has changed is not relivant.
14944  *
14945  * Fork - LGPL
14946  * <script type="text/javascript">
14947  */
14948
14949 /**
14950  * @class Roo.View
14951  * @extends Roo.util.Observable
14952  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14953  * This class also supports single and multi selection modes. <br>
14954  * Create a data model bound view:
14955  <pre><code>
14956  var store = new Roo.data.Store(...);
14957
14958  var view = new Roo.View({
14959     el : "my-element",
14960     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14961  
14962     singleSelect: true,
14963     selectedClass: "ydataview-selected",
14964     store: store
14965  });
14966
14967  // listen for node click?
14968  view.on("click", function(vw, index, node, e){
14969  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14970  });
14971
14972  // load XML data
14973  dataModel.load("foobar.xml");
14974  </code></pre>
14975  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14976  * <br><br>
14977  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14978  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14979  * 
14980  * Note: old style constructor is still suported (container, template, config)
14981  * 
14982  * @constructor
14983  * Create a new View
14984  * @param {Object} config The config object
14985  * 
14986  */
14987 Roo.View = function(config, depreciated_tpl, depreciated_config){
14988     
14989     this.parent = false;
14990     
14991     if (typeof(depreciated_tpl) == 'undefined') {
14992         // new way.. - universal constructor.
14993         Roo.apply(this, config);
14994         this.el  = Roo.get(this.el);
14995     } else {
14996         // old format..
14997         this.el  = Roo.get(config);
14998         this.tpl = depreciated_tpl;
14999         Roo.apply(this, depreciated_config);
15000     }
15001     this.wrapEl  = this.el.wrap().wrap();
15002     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15003     
15004     
15005     if(typeof(this.tpl) == "string"){
15006         this.tpl = new Roo.Template(this.tpl);
15007     } else {
15008         // support xtype ctors..
15009         this.tpl = new Roo.factory(this.tpl, Roo);
15010     }
15011     
15012     
15013     this.tpl.compile();
15014     
15015     /** @private */
15016     this.addEvents({
15017         /**
15018          * @event beforeclick
15019          * Fires before a click is processed. Returns false to cancel the default action.
15020          * @param {Roo.View} this
15021          * @param {Number} index The index of the target node
15022          * @param {HTMLElement} node The target node
15023          * @param {Roo.EventObject} e The raw event object
15024          */
15025             "beforeclick" : true,
15026         /**
15027          * @event click
15028          * Fires when a template node is clicked.
15029          * @param {Roo.View} this
15030          * @param {Number} index The index of the target node
15031          * @param {HTMLElement} node The target node
15032          * @param {Roo.EventObject} e The raw event object
15033          */
15034             "click" : true,
15035         /**
15036          * @event dblclick
15037          * Fires when a template node is double clicked.
15038          * @param {Roo.View} this
15039          * @param {Number} index The index of the target node
15040          * @param {HTMLElement} node The target node
15041          * @param {Roo.EventObject} e The raw event object
15042          */
15043             "dblclick" : true,
15044         /**
15045          * @event contextmenu
15046          * Fires when a template node is right clicked.
15047          * @param {Roo.View} this
15048          * @param {Number} index The index of the target node
15049          * @param {HTMLElement} node The target node
15050          * @param {Roo.EventObject} e The raw event object
15051          */
15052             "contextmenu" : true,
15053         /**
15054          * @event selectionchange
15055          * Fires when the selected nodes change.
15056          * @param {Roo.View} this
15057          * @param {Array} selections Array of the selected nodes
15058          */
15059             "selectionchange" : true,
15060     
15061         /**
15062          * @event beforeselect
15063          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15064          * @param {Roo.View} this
15065          * @param {HTMLElement} node The node to be selected
15066          * @param {Array} selections Array of currently selected nodes
15067          */
15068             "beforeselect" : true,
15069         /**
15070          * @event preparedata
15071          * Fires on every row to render, to allow you to change the data.
15072          * @param {Roo.View} this
15073          * @param {Object} data to be rendered (change this)
15074          */
15075           "preparedata" : true
15076           
15077           
15078         });
15079
15080
15081
15082     this.el.on({
15083         "click": this.onClick,
15084         "dblclick": this.onDblClick,
15085         "contextmenu": this.onContextMenu,
15086         scope:this
15087     });
15088
15089     this.selections = [];
15090     this.nodes = [];
15091     this.cmp = new Roo.CompositeElementLite([]);
15092     if(this.store){
15093         this.store = Roo.factory(this.store, Roo.data);
15094         this.setStore(this.store, true);
15095     }
15096     
15097     if ( this.footer && this.footer.xtype) {
15098            
15099          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15100         
15101         this.footer.dataSource = this.store;
15102         this.footer.container = fctr;
15103         this.footer = Roo.factory(this.footer, Roo);
15104         fctr.insertFirst(this.el);
15105         
15106         // this is a bit insane - as the paging toolbar seems to detach the el..
15107 //        dom.parentNode.parentNode.parentNode
15108          // they get detached?
15109     }
15110     
15111     
15112     Roo.View.superclass.constructor.call(this);
15113     
15114     
15115 };
15116
15117 Roo.extend(Roo.View, Roo.util.Observable, {
15118     
15119      /**
15120      * @cfg {Roo.data.Store} store Data store to load data from.
15121      */
15122     store : false,
15123     
15124     /**
15125      * @cfg {String|Roo.Element} el The container element.
15126      */
15127     el : '',
15128     
15129     /**
15130      * @cfg {String|Roo.Template} tpl The template used by this View 
15131      */
15132     tpl : false,
15133     /**
15134      * @cfg {String} dataName the named area of the template to use as the data area
15135      *                          Works with domtemplates roo-name="name"
15136      */
15137     dataName: false,
15138     /**
15139      * @cfg {String} selectedClass The css class to add to selected nodes
15140      */
15141     selectedClass : "x-view-selected",
15142      /**
15143      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15144      */
15145     emptyText : "",
15146     
15147     /**
15148      * @cfg {String} text to display on mask (default Loading)
15149      */
15150     mask : false,
15151     /**
15152      * @cfg {Boolean} multiSelect Allow multiple selection
15153      */
15154     multiSelect : false,
15155     /**
15156      * @cfg {Boolean} singleSelect Allow single selection
15157      */
15158     singleSelect:  false,
15159     
15160     /**
15161      * @cfg {Boolean} toggleSelect - selecting 
15162      */
15163     toggleSelect : false,
15164     
15165     /**
15166      * @cfg {Boolean} tickable - selecting 
15167      */
15168     tickable : false,
15169     
15170     /**
15171      * Returns the element this view is bound to.
15172      * @return {Roo.Element}
15173      */
15174     getEl : function(){
15175         return this.wrapEl;
15176     },
15177     
15178     
15179
15180     /**
15181      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15182      */
15183     refresh : function(){
15184         //Roo.log('refresh');
15185         var t = this.tpl;
15186         
15187         // if we are using something like 'domtemplate', then
15188         // the what gets used is:
15189         // t.applySubtemplate(NAME, data, wrapping data..)
15190         // the outer template then get' applied with
15191         //     the store 'extra data'
15192         // and the body get's added to the
15193         //      roo-name="data" node?
15194         //      <span class='roo-tpl-{name}'></span> ?????
15195         
15196         
15197         
15198         this.clearSelections();
15199         this.el.update("");
15200         var html = [];
15201         var records = this.store.getRange();
15202         if(records.length < 1) {
15203             
15204             // is this valid??  = should it render a template??
15205             
15206             this.el.update(this.emptyText);
15207             return;
15208         }
15209         var el = this.el;
15210         if (this.dataName) {
15211             this.el.update(t.apply(this.store.meta)); //????
15212             el = this.el.child('.roo-tpl-' + this.dataName);
15213         }
15214         
15215         for(var i = 0, len = records.length; i < len; i++){
15216             var data = this.prepareData(records[i].data, i, records[i]);
15217             this.fireEvent("preparedata", this, data, i, records[i]);
15218             
15219             var d = Roo.apply({}, data);
15220             
15221             if(this.tickable){
15222                 Roo.apply(d, {'roo-id' : Roo.id()});
15223                 
15224                 var _this = this;
15225             
15226                 Roo.each(this.parent.item, function(item){
15227                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15228                         return;
15229                     }
15230                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15231                 });
15232             }
15233             
15234             html[html.length] = Roo.util.Format.trim(
15235                 this.dataName ?
15236                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15237                     t.apply(d)
15238             );
15239         }
15240         
15241         
15242         
15243         el.update(html.join(""));
15244         this.nodes = el.dom.childNodes;
15245         this.updateIndexes(0);
15246     },
15247     
15248
15249     /**
15250      * Function to override to reformat the data that is sent to
15251      * the template for each node.
15252      * DEPRICATED - use the preparedata event handler.
15253      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15254      * a JSON object for an UpdateManager bound view).
15255      */
15256     prepareData : function(data, index, record)
15257     {
15258         this.fireEvent("preparedata", this, data, index, record);
15259         return data;
15260     },
15261
15262     onUpdate : function(ds, record){
15263         // Roo.log('on update');   
15264         this.clearSelections();
15265         var index = this.store.indexOf(record);
15266         var n = this.nodes[index];
15267         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15268         n.parentNode.removeChild(n);
15269         this.updateIndexes(index, index);
15270     },
15271
15272     
15273     
15274 // --------- FIXME     
15275     onAdd : function(ds, records, index)
15276     {
15277         //Roo.log(['on Add', ds, records, index] );        
15278         this.clearSelections();
15279         if(this.nodes.length == 0){
15280             this.refresh();
15281             return;
15282         }
15283         var n = this.nodes[index];
15284         for(var i = 0, len = records.length; i < len; i++){
15285             var d = this.prepareData(records[i].data, i, records[i]);
15286             if(n){
15287                 this.tpl.insertBefore(n, d);
15288             }else{
15289                 
15290                 this.tpl.append(this.el, d);
15291             }
15292         }
15293         this.updateIndexes(index);
15294     },
15295
15296     onRemove : function(ds, record, index){
15297        // Roo.log('onRemove');
15298         this.clearSelections();
15299         var el = this.dataName  ?
15300             this.el.child('.roo-tpl-' + this.dataName) :
15301             this.el; 
15302         
15303         el.dom.removeChild(this.nodes[index]);
15304         this.updateIndexes(index);
15305     },
15306
15307     /**
15308      * Refresh an individual node.
15309      * @param {Number} index
15310      */
15311     refreshNode : function(index){
15312         this.onUpdate(this.store, this.store.getAt(index));
15313     },
15314
15315     updateIndexes : function(startIndex, endIndex){
15316         var ns = this.nodes;
15317         startIndex = startIndex || 0;
15318         endIndex = endIndex || ns.length - 1;
15319         for(var i = startIndex; i <= endIndex; i++){
15320             ns[i].nodeIndex = i;
15321         }
15322     },
15323
15324     /**
15325      * Changes the data store this view uses and refresh the view.
15326      * @param {Store} store
15327      */
15328     setStore : function(store, initial){
15329         if(!initial && this.store){
15330             this.store.un("datachanged", this.refresh);
15331             this.store.un("add", this.onAdd);
15332             this.store.un("remove", this.onRemove);
15333             this.store.un("update", this.onUpdate);
15334             this.store.un("clear", this.refresh);
15335             this.store.un("beforeload", this.onBeforeLoad);
15336             this.store.un("load", this.onLoad);
15337             this.store.un("loadexception", this.onLoad);
15338         }
15339         if(store){
15340           
15341             store.on("datachanged", this.refresh, this);
15342             store.on("add", this.onAdd, this);
15343             store.on("remove", this.onRemove, this);
15344             store.on("update", this.onUpdate, this);
15345             store.on("clear", this.refresh, this);
15346             store.on("beforeload", this.onBeforeLoad, this);
15347             store.on("load", this.onLoad, this);
15348             store.on("loadexception", this.onLoad, this);
15349         }
15350         
15351         if(store){
15352             this.refresh();
15353         }
15354     },
15355     /**
15356      * onbeforeLoad - masks the loading area.
15357      *
15358      */
15359     onBeforeLoad : function(store,opts)
15360     {
15361          //Roo.log('onBeforeLoad');   
15362         if (!opts.add) {
15363             this.el.update("");
15364         }
15365         this.el.mask(this.mask ? this.mask : "Loading" ); 
15366     },
15367     onLoad : function ()
15368     {
15369         this.el.unmask();
15370     },
15371     
15372
15373     /**
15374      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15375      * @param {HTMLElement} node
15376      * @return {HTMLElement} The template node
15377      */
15378     findItemFromChild : function(node){
15379         var el = this.dataName  ?
15380             this.el.child('.roo-tpl-' + this.dataName,true) :
15381             this.el.dom; 
15382         
15383         if(!node || node.parentNode == el){
15384                     return node;
15385             }
15386             var p = node.parentNode;
15387             while(p && p != el){
15388             if(p.parentNode == el){
15389                 return p;
15390             }
15391             p = p.parentNode;
15392         }
15393             return null;
15394     },
15395
15396     /** @ignore */
15397     onClick : function(e){
15398         var item = this.findItemFromChild(e.getTarget());
15399         if(item){
15400             var index = this.indexOf(item);
15401             if(this.onItemClick(item, index, e) !== false){
15402                 this.fireEvent("click", this, index, item, e);
15403             }
15404         }else{
15405             this.clearSelections();
15406         }
15407     },
15408
15409     /** @ignore */
15410     onContextMenu : function(e){
15411         var item = this.findItemFromChild(e.getTarget());
15412         if(item){
15413             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15414         }
15415     },
15416
15417     /** @ignore */
15418     onDblClick : function(e){
15419         var item = this.findItemFromChild(e.getTarget());
15420         if(item){
15421             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15422         }
15423     },
15424
15425     onItemClick : function(item, index, e)
15426     {
15427         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15428             return false;
15429         }
15430         if (this.toggleSelect) {
15431             var m = this.isSelected(item) ? 'unselect' : 'select';
15432             //Roo.log(m);
15433             var _t = this;
15434             _t[m](item, true, false);
15435             return true;
15436         }
15437         if(this.multiSelect || this.singleSelect){
15438             if(this.multiSelect && e.shiftKey && this.lastSelection){
15439                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15440             }else{
15441                 this.select(item, this.multiSelect && e.ctrlKey);
15442                 this.lastSelection = item;
15443             }
15444             
15445             if(!this.tickable){
15446                 e.preventDefault();
15447             }
15448             
15449         }
15450         return true;
15451     },
15452
15453     /**
15454      * Get the number of selected nodes.
15455      * @return {Number}
15456      */
15457     getSelectionCount : function(){
15458         return this.selections.length;
15459     },
15460
15461     /**
15462      * Get the currently selected nodes.
15463      * @return {Array} An array of HTMLElements
15464      */
15465     getSelectedNodes : function(){
15466         return this.selections;
15467     },
15468
15469     /**
15470      * Get the indexes of the selected nodes.
15471      * @return {Array}
15472      */
15473     getSelectedIndexes : function(){
15474         var indexes = [], s = this.selections;
15475         for(var i = 0, len = s.length; i < len; i++){
15476             indexes.push(s[i].nodeIndex);
15477         }
15478         return indexes;
15479     },
15480
15481     /**
15482      * Clear all selections
15483      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15484      */
15485     clearSelections : function(suppressEvent){
15486         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15487             this.cmp.elements = this.selections;
15488             this.cmp.removeClass(this.selectedClass);
15489             this.selections = [];
15490             if(!suppressEvent){
15491                 this.fireEvent("selectionchange", this, this.selections);
15492             }
15493         }
15494     },
15495
15496     /**
15497      * Returns true if the passed node is selected
15498      * @param {HTMLElement/Number} node The node or node index
15499      * @return {Boolean}
15500      */
15501     isSelected : function(node){
15502         var s = this.selections;
15503         if(s.length < 1){
15504             return false;
15505         }
15506         node = this.getNode(node);
15507         return s.indexOf(node) !== -1;
15508     },
15509
15510     /**
15511      * Selects nodes.
15512      * @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
15513      * @param {Boolean} keepExisting (optional) true to keep existing selections
15514      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15515      */
15516     select : function(nodeInfo, keepExisting, suppressEvent){
15517         if(nodeInfo instanceof Array){
15518             if(!keepExisting){
15519                 this.clearSelections(true);
15520             }
15521             for(var i = 0, len = nodeInfo.length; i < len; i++){
15522                 this.select(nodeInfo[i], true, true);
15523             }
15524             return;
15525         } 
15526         var node = this.getNode(nodeInfo);
15527         if(!node || this.isSelected(node)){
15528             return; // already selected.
15529         }
15530         if(!keepExisting){
15531             this.clearSelections(true);
15532         }
15533         
15534         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15535             Roo.fly(node).addClass(this.selectedClass);
15536             this.selections.push(node);
15537             if(!suppressEvent){
15538                 this.fireEvent("selectionchange", this, this.selections);
15539             }
15540         }
15541         
15542         
15543     },
15544       /**
15545      * Unselects nodes.
15546      * @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
15547      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15548      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15549      */
15550     unselect : function(nodeInfo, keepExisting, suppressEvent)
15551     {
15552         if(nodeInfo instanceof Array){
15553             Roo.each(this.selections, function(s) {
15554                 this.unselect(s, nodeInfo);
15555             }, this);
15556             return;
15557         }
15558         var node = this.getNode(nodeInfo);
15559         if(!node || !this.isSelected(node)){
15560             //Roo.log("not selected");
15561             return; // not selected.
15562         }
15563         // fireevent???
15564         var ns = [];
15565         Roo.each(this.selections, function(s) {
15566             if (s == node ) {
15567                 Roo.fly(node).removeClass(this.selectedClass);
15568
15569                 return;
15570             }
15571             ns.push(s);
15572         },this);
15573         
15574         this.selections= ns;
15575         this.fireEvent("selectionchange", this, this.selections);
15576     },
15577
15578     /**
15579      * Gets a template node.
15580      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15581      * @return {HTMLElement} The node or null if it wasn't found
15582      */
15583     getNode : function(nodeInfo){
15584         if(typeof nodeInfo == "string"){
15585             return document.getElementById(nodeInfo);
15586         }else if(typeof nodeInfo == "number"){
15587             return this.nodes[nodeInfo];
15588         }
15589         return nodeInfo;
15590     },
15591
15592     /**
15593      * Gets a range template nodes.
15594      * @param {Number} startIndex
15595      * @param {Number} endIndex
15596      * @return {Array} An array of nodes
15597      */
15598     getNodes : function(start, end){
15599         var ns = this.nodes;
15600         start = start || 0;
15601         end = typeof end == "undefined" ? ns.length - 1 : end;
15602         var nodes = [];
15603         if(start <= end){
15604             for(var i = start; i <= end; i++){
15605                 nodes.push(ns[i]);
15606             }
15607         } else{
15608             for(var i = start; i >= end; i--){
15609                 nodes.push(ns[i]);
15610             }
15611         }
15612         return nodes;
15613     },
15614
15615     /**
15616      * Finds the index of the passed node
15617      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15618      * @return {Number} The index of the node or -1
15619      */
15620     indexOf : function(node){
15621         node = this.getNode(node);
15622         if(typeof node.nodeIndex == "number"){
15623             return node.nodeIndex;
15624         }
15625         var ns = this.nodes;
15626         for(var i = 0, len = ns.length; i < len; i++){
15627             if(ns[i] == node){
15628                 return i;
15629             }
15630         }
15631         return -1;
15632     }
15633 });
15634 /*
15635  * - LGPL
15636  *
15637  * based on jquery fullcalendar
15638  * 
15639  */
15640
15641 Roo.bootstrap = Roo.bootstrap || {};
15642 /**
15643  * @class Roo.bootstrap.Calendar
15644  * @extends Roo.bootstrap.Component
15645  * Bootstrap Calendar class
15646  * @cfg {Boolean} loadMask (true|false) default false
15647  * @cfg {Object} header generate the user specific header of the calendar, default false
15648
15649  * @constructor
15650  * Create a new Container
15651  * @param {Object} config The config object
15652  */
15653
15654
15655
15656 Roo.bootstrap.Calendar = function(config){
15657     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15658      this.addEvents({
15659         /**
15660              * @event select
15661              * Fires when a date is selected
15662              * @param {DatePicker} this
15663              * @param {Date} date The selected date
15664              */
15665         'select': true,
15666         /**
15667              * @event monthchange
15668              * Fires when the displayed month changes 
15669              * @param {DatePicker} this
15670              * @param {Date} date The selected month
15671              */
15672         'monthchange': true,
15673         /**
15674              * @event evententer
15675              * Fires when mouse over an event
15676              * @param {Calendar} this
15677              * @param {event} Event
15678              */
15679         'evententer': true,
15680         /**
15681              * @event eventleave
15682              * Fires when the mouse leaves an
15683              * @param {Calendar} this
15684              * @param {event}
15685              */
15686         'eventleave': true,
15687         /**
15688              * @event eventclick
15689              * Fires when the mouse click an
15690              * @param {Calendar} this
15691              * @param {event}
15692              */
15693         'eventclick': true
15694         
15695     });
15696
15697 };
15698
15699 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15700     
15701      /**
15702      * @cfg {Number} startDay
15703      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15704      */
15705     startDay : 0,
15706     
15707     loadMask : false,
15708     
15709     header : false,
15710       
15711     getAutoCreate : function(){
15712         
15713         
15714         var fc_button = function(name, corner, style, content ) {
15715             return Roo.apply({},{
15716                 tag : 'span',
15717                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15718                          (corner.length ?
15719                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15720                             ''
15721                         ),
15722                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15723                 unselectable: 'on'
15724             });
15725         };
15726         
15727         var header = {};
15728         
15729         if(!this.header){
15730             header = {
15731                 tag : 'table',
15732                 cls : 'fc-header',
15733                 style : 'width:100%',
15734                 cn : [
15735                     {
15736                         tag: 'tr',
15737                         cn : [
15738                             {
15739                                 tag : 'td',
15740                                 cls : 'fc-header-left',
15741                                 cn : [
15742                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15743                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15744                                     { tag: 'span', cls: 'fc-header-space' },
15745                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15746
15747
15748                                 ]
15749                             },
15750
15751                             {
15752                                 tag : 'td',
15753                                 cls : 'fc-header-center',
15754                                 cn : [
15755                                     {
15756                                         tag: 'span',
15757                                         cls: 'fc-header-title',
15758                                         cn : {
15759                                             tag: 'H2',
15760                                             html : 'month / year'
15761                                         }
15762                                     }
15763
15764                                 ]
15765                             },
15766                             {
15767                                 tag : 'td',
15768                                 cls : 'fc-header-right',
15769                                 cn : [
15770                               /*      fc_button('month', 'left', '', 'month' ),
15771                                     fc_button('week', '', '', 'week' ),
15772                                     fc_button('day', 'right', '', 'day' )
15773                                 */    
15774
15775                                 ]
15776                             }
15777
15778                         ]
15779                     }
15780                 ]
15781             };
15782         }
15783         
15784         header = this.header;
15785         
15786        
15787         var cal_heads = function() {
15788             var ret = [];
15789             // fixme - handle this.
15790             
15791             for (var i =0; i < Date.dayNames.length; i++) {
15792                 var d = Date.dayNames[i];
15793                 ret.push({
15794                     tag: 'th',
15795                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15796                     html : d.substring(0,3)
15797                 });
15798                 
15799             }
15800             ret[0].cls += ' fc-first';
15801             ret[6].cls += ' fc-last';
15802             return ret;
15803         };
15804         var cal_cell = function(n) {
15805             return  {
15806                 tag: 'td',
15807                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15808                 cn : [
15809                     {
15810                         cn : [
15811                             {
15812                                 cls: 'fc-day-number',
15813                                 html: 'D'
15814                             },
15815                             {
15816                                 cls: 'fc-day-content',
15817                              
15818                                 cn : [
15819                                      {
15820                                         style: 'position: relative;' // height: 17px;
15821                                     }
15822                                 ]
15823                             }
15824                             
15825                             
15826                         ]
15827                     }
15828                 ]
15829                 
15830             }
15831         };
15832         var cal_rows = function() {
15833             
15834             var ret = [];
15835             for (var r = 0; r < 6; r++) {
15836                 var row= {
15837                     tag : 'tr',
15838                     cls : 'fc-week',
15839                     cn : []
15840                 };
15841                 
15842                 for (var i =0; i < Date.dayNames.length; i++) {
15843                     var d = Date.dayNames[i];
15844                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15845
15846                 }
15847                 row.cn[0].cls+=' fc-first';
15848                 row.cn[0].cn[0].style = 'min-height:90px';
15849                 row.cn[6].cls+=' fc-last';
15850                 ret.push(row);
15851                 
15852             }
15853             ret[0].cls += ' fc-first';
15854             ret[4].cls += ' fc-prev-last';
15855             ret[5].cls += ' fc-last';
15856             return ret;
15857             
15858         };
15859         
15860         var cal_table = {
15861             tag: 'table',
15862             cls: 'fc-border-separate',
15863             style : 'width:100%',
15864             cellspacing  : 0,
15865             cn : [
15866                 { 
15867                     tag: 'thead',
15868                     cn : [
15869                         { 
15870                             tag: 'tr',
15871                             cls : 'fc-first fc-last',
15872                             cn : cal_heads()
15873                         }
15874                     ]
15875                 },
15876                 { 
15877                     tag: 'tbody',
15878                     cn : cal_rows()
15879                 }
15880                   
15881             ]
15882         };
15883          
15884          var cfg = {
15885             cls : 'fc fc-ltr',
15886             cn : [
15887                 header,
15888                 {
15889                     cls : 'fc-content',
15890                     style : "position: relative;",
15891                     cn : [
15892                         {
15893                             cls : 'fc-view fc-view-month fc-grid',
15894                             style : 'position: relative',
15895                             unselectable : 'on',
15896                             cn : [
15897                                 {
15898                                     cls : 'fc-event-container',
15899                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15900                                 },
15901                                 cal_table
15902                             ]
15903                         }
15904                     ]
15905     
15906                 }
15907            ] 
15908             
15909         };
15910         
15911          
15912         
15913         return cfg;
15914     },
15915     
15916     
15917     initEvents : function()
15918     {
15919         if(!this.store){
15920             throw "can not find store for calendar";
15921         }
15922         
15923         var mark = {
15924             tag: "div",
15925             cls:"x-dlg-mask",
15926             style: "text-align:center",
15927             cn: [
15928                 {
15929                     tag: "div",
15930                     style: "background-color:white;width:50%;margin:250 auto",
15931                     cn: [
15932                         {
15933                             tag: "img",
15934                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15935                         },
15936                         {
15937                             tag: "span",
15938                             html: "Loading"
15939                         }
15940                         
15941                     ]
15942                 }
15943             ]
15944         };
15945         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15946         
15947         var size = this.el.select('.fc-content', true).first().getSize();
15948         this.maskEl.setSize(size.width, size.height);
15949         this.maskEl.enableDisplayMode("block");
15950         if(!this.loadMask){
15951             this.maskEl.hide();
15952         }
15953         
15954         this.store = Roo.factory(this.store, Roo.data);
15955         this.store.on('load', this.onLoad, this);
15956         this.store.on('beforeload', this.onBeforeLoad, this);
15957         
15958         this.resize();
15959         
15960         this.cells = this.el.select('.fc-day',true);
15961         //Roo.log(this.cells);
15962         this.textNodes = this.el.query('.fc-day-number');
15963         this.cells.addClassOnOver('fc-state-hover');
15964         
15965         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15966         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15967         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15968         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15969         
15970         this.on('monthchange', this.onMonthChange, this);
15971         
15972         this.update(new Date().clearTime());
15973     },
15974     
15975     resize : function() {
15976         var sz  = this.el.getSize();
15977         
15978         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15979         this.el.select('.fc-day-content div',true).setHeight(34);
15980     },
15981     
15982     
15983     // private
15984     showPrevMonth : function(e){
15985         this.update(this.activeDate.add("mo", -1));
15986     },
15987     showToday : function(e){
15988         this.update(new Date().clearTime());
15989     },
15990     // private
15991     showNextMonth : function(e){
15992         this.update(this.activeDate.add("mo", 1));
15993     },
15994
15995     // private
15996     showPrevYear : function(){
15997         this.update(this.activeDate.add("y", -1));
15998     },
15999
16000     // private
16001     showNextYear : function(){
16002         this.update(this.activeDate.add("y", 1));
16003     },
16004
16005     
16006    // private
16007     update : function(date)
16008     {
16009         var vd = this.activeDate;
16010         this.activeDate = date;
16011 //        if(vd && this.el){
16012 //            var t = date.getTime();
16013 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16014 //                Roo.log('using add remove');
16015 //                
16016 //                this.fireEvent('monthchange', this, date);
16017 //                
16018 //                this.cells.removeClass("fc-state-highlight");
16019 //                this.cells.each(function(c){
16020 //                   if(c.dateValue == t){
16021 //                       c.addClass("fc-state-highlight");
16022 //                       setTimeout(function(){
16023 //                            try{c.dom.firstChild.focus();}catch(e){}
16024 //                       }, 50);
16025 //                       return false;
16026 //                   }
16027 //                   return true;
16028 //                });
16029 //                return;
16030 //            }
16031 //        }
16032         
16033         var days = date.getDaysInMonth();
16034         
16035         var firstOfMonth = date.getFirstDateOfMonth();
16036         var startingPos = firstOfMonth.getDay()-this.startDay;
16037         
16038         if(startingPos < this.startDay){
16039             startingPos += 7;
16040         }
16041         
16042         var pm = date.add(Date.MONTH, -1);
16043         var prevStart = pm.getDaysInMonth()-startingPos;
16044 //        
16045         this.cells = this.el.select('.fc-day',true);
16046         this.textNodes = this.el.query('.fc-day-number');
16047         this.cells.addClassOnOver('fc-state-hover');
16048         
16049         var cells = this.cells.elements;
16050         var textEls = this.textNodes;
16051         
16052         Roo.each(cells, function(cell){
16053             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16054         });
16055         
16056         days += startingPos;
16057
16058         // convert everything to numbers so it's fast
16059         var day = 86400000;
16060         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16061         //Roo.log(d);
16062         //Roo.log(pm);
16063         //Roo.log(prevStart);
16064         
16065         var today = new Date().clearTime().getTime();
16066         var sel = date.clearTime().getTime();
16067         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16068         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16069         var ddMatch = this.disabledDatesRE;
16070         var ddText = this.disabledDatesText;
16071         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16072         var ddaysText = this.disabledDaysText;
16073         var format = this.format;
16074         
16075         var setCellClass = function(cal, cell){
16076             cell.row = 0;
16077             cell.events = [];
16078             cell.more = [];
16079             //Roo.log('set Cell Class');
16080             cell.title = "";
16081             var t = d.getTime();
16082             
16083             //Roo.log(d);
16084             
16085             cell.dateValue = t;
16086             if(t == today){
16087                 cell.className += " fc-today";
16088                 cell.className += " fc-state-highlight";
16089                 cell.title = cal.todayText;
16090             }
16091             if(t == sel){
16092                 // disable highlight in other month..
16093                 //cell.className += " fc-state-highlight";
16094                 
16095             }
16096             // disabling
16097             if(t < min) {
16098                 cell.className = " fc-state-disabled";
16099                 cell.title = cal.minText;
16100                 return;
16101             }
16102             if(t > max) {
16103                 cell.className = " fc-state-disabled";
16104                 cell.title = cal.maxText;
16105                 return;
16106             }
16107             if(ddays){
16108                 if(ddays.indexOf(d.getDay()) != -1){
16109                     cell.title = ddaysText;
16110                     cell.className = " fc-state-disabled";
16111                 }
16112             }
16113             if(ddMatch && format){
16114                 var fvalue = d.dateFormat(format);
16115                 if(ddMatch.test(fvalue)){
16116                     cell.title = ddText.replace("%0", fvalue);
16117                     cell.className = " fc-state-disabled";
16118                 }
16119             }
16120             
16121             if (!cell.initialClassName) {
16122                 cell.initialClassName = cell.dom.className;
16123             }
16124             
16125             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16126         };
16127
16128         var i = 0;
16129         
16130         for(; i < startingPos; i++) {
16131             textEls[i].innerHTML = (++prevStart);
16132             d.setDate(d.getDate()+1);
16133             
16134             cells[i].className = "fc-past fc-other-month";
16135             setCellClass(this, cells[i]);
16136         }
16137         
16138         var intDay = 0;
16139         
16140         for(; i < days; i++){
16141             intDay = i - startingPos + 1;
16142             textEls[i].innerHTML = (intDay);
16143             d.setDate(d.getDate()+1);
16144             
16145             cells[i].className = ''; // "x-date-active";
16146             setCellClass(this, cells[i]);
16147         }
16148         var extraDays = 0;
16149         
16150         for(; i < 42; i++) {
16151             textEls[i].innerHTML = (++extraDays);
16152             d.setDate(d.getDate()+1);
16153             
16154             cells[i].className = "fc-future fc-other-month";
16155             setCellClass(this, cells[i]);
16156         }
16157         
16158         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16159         
16160         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16161         
16162         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16163         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16164         
16165         if(totalRows != 6){
16166             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16167             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16168         }
16169         
16170         this.fireEvent('monthchange', this, date);
16171         
16172         
16173         /*
16174         if(!this.internalRender){
16175             var main = this.el.dom.firstChild;
16176             var w = main.offsetWidth;
16177             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16178             Roo.fly(main).setWidth(w);
16179             this.internalRender = true;
16180             // opera does not respect the auto grow header center column
16181             // then, after it gets a width opera refuses to recalculate
16182             // without a second pass
16183             if(Roo.isOpera && !this.secondPass){
16184                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16185                 this.secondPass = true;
16186                 this.update.defer(10, this, [date]);
16187             }
16188         }
16189         */
16190         
16191     },
16192     
16193     findCell : function(dt) {
16194         dt = dt.clearTime().getTime();
16195         var ret = false;
16196         this.cells.each(function(c){
16197             //Roo.log("check " +c.dateValue + '?=' + dt);
16198             if(c.dateValue == dt){
16199                 ret = c;
16200                 return false;
16201             }
16202             return true;
16203         });
16204         
16205         return ret;
16206     },
16207     
16208     findCells : function(ev) {
16209         var s = ev.start.clone().clearTime().getTime();
16210        // Roo.log(s);
16211         var e= ev.end.clone().clearTime().getTime();
16212        // Roo.log(e);
16213         var ret = [];
16214         this.cells.each(function(c){
16215              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16216             
16217             if(c.dateValue > e){
16218                 return ;
16219             }
16220             if(c.dateValue < s){
16221                 return ;
16222             }
16223             ret.push(c);
16224         });
16225         
16226         return ret;    
16227     },
16228     
16229 //    findBestRow: function(cells)
16230 //    {
16231 //        var ret = 0;
16232 //        
16233 //        for (var i =0 ; i < cells.length;i++) {
16234 //            ret  = Math.max(cells[i].rows || 0,ret);
16235 //        }
16236 //        return ret;
16237 //        
16238 //    },
16239     
16240     
16241     addItem : function(ev)
16242     {
16243         // look for vertical location slot in
16244         var cells = this.findCells(ev);
16245         
16246 //        ev.row = this.findBestRow(cells);
16247         
16248         // work out the location.
16249         
16250         var crow = false;
16251         var rows = [];
16252         for(var i =0; i < cells.length; i++) {
16253             
16254             cells[i].row = cells[0].row;
16255             
16256             if(i == 0){
16257                 cells[i].row = cells[i].row + 1;
16258             }
16259             
16260             if (!crow) {
16261                 crow = {
16262                     start : cells[i],
16263                     end :  cells[i]
16264                 };
16265                 continue;
16266             }
16267             if (crow.start.getY() == cells[i].getY()) {
16268                 // on same row.
16269                 crow.end = cells[i];
16270                 continue;
16271             }
16272             // different row.
16273             rows.push(crow);
16274             crow = {
16275                 start: cells[i],
16276                 end : cells[i]
16277             };
16278             
16279         }
16280         
16281         rows.push(crow);
16282         ev.els = [];
16283         ev.rows = rows;
16284         ev.cells = cells;
16285         
16286         cells[0].events.push(ev);
16287         
16288         this.calevents.push(ev);
16289     },
16290     
16291     clearEvents: function() {
16292         
16293         if(!this.calevents){
16294             return;
16295         }
16296         
16297         Roo.each(this.cells.elements, function(c){
16298             c.row = 0;
16299             c.events = [];
16300             c.more = [];
16301         });
16302         
16303         Roo.each(this.calevents, function(e) {
16304             Roo.each(e.els, function(el) {
16305                 el.un('mouseenter' ,this.onEventEnter, this);
16306                 el.un('mouseleave' ,this.onEventLeave, this);
16307                 el.remove();
16308             },this);
16309         },this);
16310         
16311         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16312             e.remove();
16313         });
16314         
16315     },
16316     
16317     renderEvents: function()
16318     {   
16319         var _this = this;
16320         
16321         this.cells.each(function(c) {
16322             
16323             if(c.row < 5){
16324                 return;
16325             }
16326             
16327             var ev = c.events;
16328             
16329             var r = 4;
16330             if(c.row != c.events.length){
16331                 r = 4 - (4 - (c.row - c.events.length));
16332             }
16333             
16334             c.events = ev.slice(0, r);
16335             c.more = ev.slice(r);
16336             
16337             if(c.more.length && c.more.length == 1){
16338                 c.events.push(c.more.pop());
16339             }
16340             
16341             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16342             
16343         });
16344             
16345         this.cells.each(function(c) {
16346             
16347             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16348             
16349             
16350             for (var e = 0; e < c.events.length; e++){
16351                 var ev = c.events[e];
16352                 var rows = ev.rows;
16353                 
16354                 for(var i = 0; i < rows.length; i++) {
16355                 
16356                     // how many rows should it span..
16357
16358                     var  cfg = {
16359                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16360                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16361
16362                         unselectable : "on",
16363                         cn : [
16364                             {
16365                                 cls: 'fc-event-inner',
16366                                 cn : [
16367     //                                {
16368     //                                  tag:'span',
16369     //                                  cls: 'fc-event-time',
16370     //                                  html : cells.length > 1 ? '' : ev.time
16371     //                                },
16372                                     {
16373                                       tag:'span',
16374                                       cls: 'fc-event-title',
16375                                       html : String.format('{0}', ev.title)
16376                                     }
16377
16378
16379                                 ]
16380                             },
16381                             {
16382                                 cls: 'ui-resizable-handle ui-resizable-e',
16383                                 html : '&nbsp;&nbsp;&nbsp'
16384                             }
16385
16386                         ]
16387                     };
16388
16389                     if (i == 0) {
16390                         cfg.cls += ' fc-event-start';
16391                     }
16392                     if ((i+1) == rows.length) {
16393                         cfg.cls += ' fc-event-end';
16394                     }
16395
16396                     var ctr = _this.el.select('.fc-event-container',true).first();
16397                     var cg = ctr.createChild(cfg);
16398
16399                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16400                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16401
16402                     var r = (c.more.length) ? 1 : 0;
16403                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16404                     cg.setWidth(ebox.right - sbox.x -2);
16405
16406                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16407                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16408                     cg.on('click', _this.onEventClick, _this, ev);
16409
16410                     ev.els.push(cg);
16411                     
16412                 }
16413                 
16414             }
16415             
16416             
16417             if(c.more.length){
16418                 var  cfg = {
16419                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16420                     style : 'position: absolute',
16421                     unselectable : "on",
16422                     cn : [
16423                         {
16424                             cls: 'fc-event-inner',
16425                             cn : [
16426                                 {
16427                                   tag:'span',
16428                                   cls: 'fc-event-title',
16429                                   html : 'More'
16430                                 }
16431
16432
16433                             ]
16434                         },
16435                         {
16436                             cls: 'ui-resizable-handle ui-resizable-e',
16437                             html : '&nbsp;&nbsp;&nbsp'
16438                         }
16439
16440                     ]
16441                 };
16442
16443                 var ctr = _this.el.select('.fc-event-container',true).first();
16444                 var cg = ctr.createChild(cfg);
16445
16446                 var sbox = c.select('.fc-day-content',true).first().getBox();
16447                 var ebox = c.select('.fc-day-content',true).first().getBox();
16448                 //Roo.log(cg);
16449                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16450                 cg.setWidth(ebox.right - sbox.x -2);
16451
16452                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16453                 
16454             }
16455             
16456         });
16457         
16458         
16459         
16460     },
16461     
16462     onEventEnter: function (e, el,event,d) {
16463         this.fireEvent('evententer', this, el, event);
16464     },
16465     
16466     onEventLeave: function (e, el,event,d) {
16467         this.fireEvent('eventleave', this, el, event);
16468     },
16469     
16470     onEventClick: function (e, el,event,d) {
16471         this.fireEvent('eventclick', this, el, event);
16472     },
16473     
16474     onMonthChange: function () {
16475         this.store.load();
16476     },
16477     
16478     onMoreEventClick: function(e, el, more)
16479     {
16480         var _this = this;
16481         
16482         this.calpopover.placement = 'right';
16483         this.calpopover.setTitle('More');
16484         
16485         this.calpopover.setContent('');
16486         
16487         var ctr = this.calpopover.el.select('.popover-content', true).first();
16488         
16489         Roo.each(more, function(m){
16490             var cfg = {
16491                 cls : 'fc-event-hori fc-event-draggable',
16492                 html : m.title
16493             };
16494             var cg = ctr.createChild(cfg);
16495             
16496             cg.on('click', _this.onEventClick, _this, m);
16497         });
16498         
16499         this.calpopover.show(el);
16500         
16501         
16502     },
16503     
16504     onLoad: function () 
16505     {   
16506         this.calevents = [];
16507         var cal = this;
16508         
16509         if(this.store.getCount() > 0){
16510             this.store.data.each(function(d){
16511                cal.addItem({
16512                     id : d.data.id,
16513                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16514                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16515                     time : d.data.start_time,
16516                     title : d.data.title,
16517                     description : d.data.description,
16518                     venue : d.data.venue
16519                 });
16520             });
16521         }
16522         
16523         this.renderEvents();
16524         
16525         if(this.calevents.length && this.loadMask){
16526             this.maskEl.hide();
16527         }
16528     },
16529     
16530     onBeforeLoad: function()
16531     {
16532         this.clearEvents();
16533         if(this.loadMask){
16534             this.maskEl.show();
16535         }
16536     }
16537 });
16538
16539  
16540  /*
16541  * - LGPL
16542  *
16543  * element
16544  * 
16545  */
16546
16547 /**
16548  * @class Roo.bootstrap.Popover
16549  * @extends Roo.bootstrap.Component
16550  * Bootstrap Popover class
16551  * @cfg {String} html contents of the popover   (or false to use children..)
16552  * @cfg {String} title of popover (or false to hide)
16553  * @cfg {String} placement how it is placed
16554  * @cfg {String} trigger click || hover (or false to trigger manually)
16555  * @cfg {String} over what (parent or false to trigger manually.)
16556  * @cfg {Number} delay - delay before showing
16557  
16558  * @constructor
16559  * Create a new Popover
16560  * @param {Object} config The config object
16561  */
16562
16563 Roo.bootstrap.Popover = function(config){
16564     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16565     
16566     this.addEvents({
16567         // raw events
16568          /**
16569          * @event show
16570          * After the popover show
16571          * 
16572          * @param {Roo.bootstrap.Popover} this
16573          */
16574         "show" : true,
16575         /**
16576          * @event hide
16577          * After the popover hide
16578          * 
16579          * @param {Roo.bootstrap.Popover} this
16580          */
16581         "hide" : true
16582     });
16583 };
16584
16585 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16586     
16587     title: 'Fill in a title',
16588     html: false,
16589     
16590     placement : 'right',
16591     trigger : 'hover', // hover
16592     
16593     delay : 0,
16594     
16595     over: 'parent',
16596     
16597     can_build_overlaid : false,
16598     
16599     getChildContainer : function()
16600     {
16601         return this.el.select('.popover-content',true).first();
16602     },
16603     
16604     getAutoCreate : function(){
16605          
16606         var cfg = {
16607            cls : 'popover roo-dynamic',
16608            style: 'display:block',
16609            cn : [
16610                 {
16611                     cls : 'arrow'
16612                 },
16613                 {
16614                     cls : 'popover-inner',
16615                     cn : [
16616                         {
16617                             tag: 'h3',
16618                             cls: 'popover-title',
16619                             html : this.title
16620                         },
16621                         {
16622                             cls : 'popover-content',
16623                             html : this.html
16624                         }
16625                     ]
16626                     
16627                 }
16628            ]
16629         };
16630         
16631         return cfg;
16632     },
16633     setTitle: function(str)
16634     {
16635         this.title = str;
16636         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16637     },
16638     setContent: function(str)
16639     {
16640         this.html = str;
16641         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16642     },
16643     // as it get's added to the bottom of the page.
16644     onRender : function(ct, position)
16645     {
16646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16647         if(!this.el){
16648             var cfg = Roo.apply({},  this.getAutoCreate());
16649             cfg.id = Roo.id();
16650             
16651             if (this.cls) {
16652                 cfg.cls += ' ' + this.cls;
16653             }
16654             if (this.style) {
16655                 cfg.style = this.style;
16656             }
16657             //Roo.log("adding to ");
16658             this.el = Roo.get(document.body).createChild(cfg, position);
16659 //            Roo.log(this.el);
16660         }
16661         this.initEvents();
16662     },
16663     
16664     initEvents : function()
16665     {
16666         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16667         this.el.enableDisplayMode('block');
16668         this.el.hide();
16669         if (this.over === false) {
16670             return; 
16671         }
16672         if (this.triggers === false) {
16673             return;
16674         }
16675         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16676         var triggers = this.trigger ? this.trigger.split(' ') : [];
16677         Roo.each(triggers, function(trigger) {
16678         
16679             if (trigger == 'click') {
16680                 on_el.on('click', this.toggle, this);
16681             } else if (trigger != 'manual') {
16682                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16683                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16684       
16685                 on_el.on(eventIn  ,this.enter, this);
16686                 on_el.on(eventOut, this.leave, this);
16687             }
16688         }, this);
16689         
16690     },
16691     
16692     
16693     // private
16694     timeout : null,
16695     hoverState : null,
16696     
16697     toggle : function () {
16698         this.hoverState == 'in' ? this.leave() : this.enter();
16699     },
16700     
16701     enter : function () {
16702         
16703         clearTimeout(this.timeout);
16704     
16705         this.hoverState = 'in';
16706     
16707         if (!this.delay || !this.delay.show) {
16708             this.show();
16709             return;
16710         }
16711         var _t = this;
16712         this.timeout = setTimeout(function () {
16713             if (_t.hoverState == 'in') {
16714                 _t.show();
16715             }
16716         }, this.delay.show)
16717     },
16718     
16719     leave : function() {
16720         clearTimeout(this.timeout);
16721     
16722         this.hoverState = 'out';
16723     
16724         if (!this.delay || !this.delay.hide) {
16725             this.hide();
16726             return;
16727         }
16728         var _t = this;
16729         this.timeout = setTimeout(function () {
16730             if (_t.hoverState == 'out') {
16731                 _t.hide();
16732             }
16733         }, this.delay.hide)
16734     },
16735     
16736     show : function (on_el)
16737     {
16738         if (!on_el) {
16739             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16740         }
16741         
16742         // set content.
16743         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16744         if (this.html !== false) {
16745             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16746         }
16747         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16748         if (!this.title.length) {
16749             this.el.select('.popover-title',true).hide();
16750         }
16751         
16752         var placement = typeof this.placement == 'function' ?
16753             this.placement.call(this, this.el, on_el) :
16754             this.placement;
16755             
16756         var autoToken = /\s?auto?\s?/i;
16757         var autoPlace = autoToken.test(placement);
16758         if (autoPlace) {
16759             placement = placement.replace(autoToken, '') || 'top';
16760         }
16761         
16762         //this.el.detach()
16763         //this.el.setXY([0,0]);
16764         this.el.show();
16765         this.el.dom.style.display='block';
16766         this.el.addClass(placement);
16767         
16768         //this.el.appendTo(on_el);
16769         
16770         var p = this.getPosition();
16771         var box = this.el.getBox();
16772         
16773         if (autoPlace) {
16774             // fixme..
16775         }
16776         var align = Roo.bootstrap.Popover.alignment[placement];
16777         this.el.alignTo(on_el, align[0],align[1]);
16778         //var arrow = this.el.select('.arrow',true).first();
16779         //arrow.set(align[2], 
16780         
16781         this.el.addClass('in');
16782         
16783         
16784         if (this.el.hasClass('fade')) {
16785             // fade it?
16786         }
16787         
16788         this.hoverState = 'in';
16789         
16790         this.fireEvent('show', this);
16791         
16792     },
16793     hide : function()
16794     {
16795         this.el.setXY([0,0]);
16796         this.el.removeClass('in');
16797         this.el.hide();
16798         this.hoverState = null;
16799         
16800         this.fireEvent('hide', this);
16801     }
16802     
16803 });
16804
16805 Roo.bootstrap.Popover.alignment = {
16806     'left' : ['r-l', [-10,0], 'right'],
16807     'right' : ['l-r', [10,0], 'left'],
16808     'bottom' : ['t-b', [0,10], 'top'],
16809     'top' : [ 'b-t', [0,-10], 'bottom']
16810 };
16811
16812  /*
16813  * - LGPL
16814  *
16815  * Progress
16816  * 
16817  */
16818
16819 /**
16820  * @class Roo.bootstrap.Progress
16821  * @extends Roo.bootstrap.Component
16822  * Bootstrap Progress class
16823  * @cfg {Boolean} striped striped of the progress bar
16824  * @cfg {Boolean} active animated of the progress bar
16825  * 
16826  * 
16827  * @constructor
16828  * Create a new Progress
16829  * @param {Object} config The config object
16830  */
16831
16832 Roo.bootstrap.Progress = function(config){
16833     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16834 };
16835
16836 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16837     
16838     striped : false,
16839     active: false,
16840     
16841     getAutoCreate : function(){
16842         var cfg = {
16843             tag: 'div',
16844             cls: 'progress'
16845         };
16846         
16847         
16848         if(this.striped){
16849             cfg.cls += ' progress-striped';
16850         }
16851       
16852         if(this.active){
16853             cfg.cls += ' active';
16854         }
16855         
16856         
16857         return cfg;
16858     }
16859    
16860 });
16861
16862  
16863
16864  /*
16865  * - LGPL
16866  *
16867  * ProgressBar
16868  * 
16869  */
16870
16871 /**
16872  * @class Roo.bootstrap.ProgressBar
16873  * @extends Roo.bootstrap.Component
16874  * Bootstrap ProgressBar class
16875  * @cfg {Number} aria_valuenow aria-value now
16876  * @cfg {Number} aria_valuemin aria-value min
16877  * @cfg {Number} aria_valuemax aria-value max
16878  * @cfg {String} label label for the progress bar
16879  * @cfg {String} panel (success | info | warning | danger )
16880  * @cfg {String} role role of the progress bar
16881  * @cfg {String} sr_only text
16882  * 
16883  * 
16884  * @constructor
16885  * Create a new ProgressBar
16886  * @param {Object} config The config object
16887  */
16888
16889 Roo.bootstrap.ProgressBar = function(config){
16890     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16891 };
16892
16893 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16894     
16895     aria_valuenow : 0,
16896     aria_valuemin : 0,
16897     aria_valuemax : 100,
16898     label : false,
16899     panel : false,
16900     role : false,
16901     sr_only: false,
16902     
16903     getAutoCreate : function()
16904     {
16905         
16906         var cfg = {
16907             tag: 'div',
16908             cls: 'progress-bar',
16909             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16910         };
16911         
16912         if(this.sr_only){
16913             cfg.cn = {
16914                 tag: 'span',
16915                 cls: 'sr-only',
16916                 html: this.sr_only
16917             }
16918         }
16919         
16920         if(this.role){
16921             cfg.role = this.role;
16922         }
16923         
16924         if(this.aria_valuenow){
16925             cfg['aria-valuenow'] = this.aria_valuenow;
16926         }
16927         
16928         if(this.aria_valuemin){
16929             cfg['aria-valuemin'] = this.aria_valuemin;
16930         }
16931         
16932         if(this.aria_valuemax){
16933             cfg['aria-valuemax'] = this.aria_valuemax;
16934         }
16935         
16936         if(this.label && !this.sr_only){
16937             cfg.html = this.label;
16938         }
16939         
16940         if(this.panel){
16941             cfg.cls += ' progress-bar-' + this.panel;
16942         }
16943         
16944         return cfg;
16945     },
16946     
16947     update : function(aria_valuenow)
16948     {
16949         this.aria_valuenow = aria_valuenow;
16950         
16951         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16952     }
16953    
16954 });
16955
16956  
16957
16958  /*
16959  * - LGPL
16960  *
16961  * column
16962  * 
16963  */
16964
16965 /**
16966  * @class Roo.bootstrap.TabGroup
16967  * @extends Roo.bootstrap.Column
16968  * Bootstrap Column class
16969  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16970  * @cfg {Boolean} carousel true to make the group behave like a carousel
16971  * @cfg {Boolean} bullets show bullets for the panels
16972  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16973  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16974  * @cfg {Boolean} showarrow (true|false) show arrow default true
16975  * 
16976  * @constructor
16977  * Create a new TabGroup
16978  * @param {Object} config The config object
16979  */
16980
16981 Roo.bootstrap.TabGroup = function(config){
16982     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16983     if (!this.navId) {
16984         this.navId = Roo.id();
16985     }
16986     this.tabs = [];
16987     Roo.bootstrap.TabGroup.register(this);
16988     
16989 };
16990
16991 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16992     
16993     carousel : false,
16994     transition : false,
16995     bullets : 0,
16996     timer : 0,
16997     autoslide : false,
16998     slideFn : false,
16999     slideOnTouch : false,
17000     showarrow : true,
17001     
17002     getAutoCreate : function()
17003     {
17004         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17005         
17006         cfg.cls += ' tab-content';
17007         
17008         if (this.carousel) {
17009             cfg.cls += ' carousel slide';
17010             
17011             cfg.cn = [{
17012                cls : 'carousel-inner',
17013                cn : []
17014             }];
17015         
17016             if(this.bullets  && !Roo.isTouch){
17017                 
17018                 var bullets = {
17019                     cls : 'carousel-bullets',
17020                     cn : []
17021                 };
17022                
17023                 if(this.bullets_cls){
17024                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17025                 }
17026                 
17027                 bullets.cn.push({
17028                     cls : 'clear'
17029                 });
17030                 
17031                 cfg.cn[0].cn.push(bullets);
17032             }
17033             
17034             if(this.showarrow){
17035                 cfg.cn[0].cn.push({
17036                     tag : 'div',
17037                     class : 'carousel-arrow',
17038                     cn : [
17039                         {
17040                             tag : 'div',
17041                             class : 'carousel-prev',
17042                             cn : [
17043                                 {
17044                                     tag : 'i',
17045                                     class : 'fa fa-chevron-left'
17046                                 }
17047                             ]
17048                         },
17049                         {
17050                             tag : 'div',
17051                             class : 'carousel-next',
17052                             cn : [
17053                                 {
17054                                     tag : 'i',
17055                                     class : 'fa fa-chevron-right'
17056                                 }
17057                             ]
17058                         }
17059                     ]
17060                 });
17061             }
17062             
17063         }
17064         
17065         return cfg;
17066     },
17067     
17068     initEvents:  function()
17069     {
17070 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17071 //            this.el.on("touchstart", this.onTouchStart, this);
17072 //        }
17073         
17074         if(this.autoslide){
17075             var _this = this;
17076             
17077             this.slideFn = window.setInterval(function() {
17078                 _this.showPanelNext();
17079             }, this.timer);
17080         }
17081         
17082         if(this.showarrow){
17083             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17084             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17085         }
17086         
17087         
17088     },
17089     
17090 //    onTouchStart : function(e, el, o)
17091 //    {
17092 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17093 //            return;
17094 //        }
17095 //        
17096 //        this.showPanelNext();
17097 //    },
17098     
17099     
17100     getChildContainer : function()
17101     {
17102         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17103     },
17104     
17105     /**
17106     * register a Navigation item
17107     * @param {Roo.bootstrap.NavItem} the navitem to add
17108     */
17109     register : function(item)
17110     {
17111         this.tabs.push( item);
17112         item.navId = this.navId; // not really needed..
17113         this.addBullet();
17114     
17115     },
17116     
17117     getActivePanel : function()
17118     {
17119         var r = false;
17120         Roo.each(this.tabs, function(t) {
17121             if (t.active) {
17122                 r = t;
17123                 return false;
17124             }
17125             return null;
17126         });
17127         return r;
17128         
17129     },
17130     getPanelByName : function(n)
17131     {
17132         var r = false;
17133         Roo.each(this.tabs, function(t) {
17134             if (t.tabId == n) {
17135                 r = t;
17136                 return false;
17137             }
17138             return null;
17139         });
17140         return r;
17141     },
17142     indexOfPanel : function(p)
17143     {
17144         var r = false;
17145         Roo.each(this.tabs, function(t,i) {
17146             if (t.tabId == p.tabId) {
17147                 r = i;
17148                 return false;
17149             }
17150             return null;
17151         });
17152         return r;
17153     },
17154     /**
17155      * show a specific panel
17156      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17157      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17158      */
17159     showPanel : function (pan)
17160     {
17161         if(this.transition || typeof(pan) == 'undefined'){
17162             Roo.log("waiting for the transitionend");
17163             return;
17164         }
17165         
17166         if (typeof(pan) == 'number') {
17167             pan = this.tabs[pan];
17168         }
17169         
17170         if (typeof(pan) == 'string') {
17171             pan = this.getPanelByName(pan);
17172         }
17173         
17174         var cur = this.getActivePanel();
17175         
17176         if(!pan || !cur){
17177             Roo.log('pan or acitve pan is undefined');
17178             return false;
17179         }
17180         
17181         if (pan.tabId == this.getActivePanel().tabId) {
17182             return true;
17183         }
17184         
17185         if (false === cur.fireEvent('beforedeactivate')) {
17186             return false;
17187         }
17188         
17189         if(this.bullets > 0 && !Roo.isTouch){
17190             this.setActiveBullet(this.indexOfPanel(pan));
17191         }
17192         
17193         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17194             
17195             this.transition = true;
17196             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17197             var lr = dir == 'next' ? 'left' : 'right';
17198             pan.el.addClass(dir); // or prev
17199             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17200             cur.el.addClass(lr); // or right
17201             pan.el.addClass(lr);
17202             
17203             var _this = this;
17204             cur.el.on('transitionend', function() {
17205                 Roo.log("trans end?");
17206                 
17207                 pan.el.removeClass([lr,dir]);
17208                 pan.setActive(true);
17209                 
17210                 cur.el.removeClass([lr]);
17211                 cur.setActive(false);
17212                 
17213                 _this.transition = false;
17214                 
17215             }, this, { single:  true } );
17216             
17217             return true;
17218         }
17219         
17220         cur.setActive(false);
17221         pan.setActive(true);
17222         
17223         return true;
17224         
17225     },
17226     showPanelNext : function()
17227     {
17228         var i = this.indexOfPanel(this.getActivePanel());
17229         
17230         if (i >= this.tabs.length - 1 && !this.autoslide) {
17231             return;
17232         }
17233         
17234         if (i >= this.tabs.length - 1 && this.autoslide) {
17235             i = -1;
17236         }
17237         
17238         this.showPanel(this.tabs[i+1]);
17239     },
17240     
17241     showPanelPrev : function()
17242     {
17243         var i = this.indexOfPanel(this.getActivePanel());
17244         
17245         if (i  < 1 && !this.autoslide) {
17246             return;
17247         }
17248         
17249         if (i < 1 && this.autoslide) {
17250             i = this.tabs.length;
17251         }
17252         
17253         this.showPanel(this.tabs[i-1]);
17254     },
17255     
17256     
17257     addBullet: function()
17258     {
17259         if(!this.bullets || Roo.isTouch){
17260             return;
17261         }
17262         var ctr = this.el.select('.carousel-bullets',true).first();
17263         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17264         var bullet = ctr.createChild({
17265             cls : 'bullet bullet-' + i
17266         },ctr.dom.lastChild);
17267         
17268         
17269         var _this = this;
17270         
17271         bullet.on('click', (function(e, el, o, ii, t){
17272
17273             e.preventDefault();
17274
17275             this.showPanel(ii);
17276
17277             if(this.autoslide && this.slideFn){
17278                 clearInterval(this.slideFn);
17279                 this.slideFn = window.setInterval(function() {
17280                     _this.showPanelNext();
17281                 }, this.timer);
17282             }
17283
17284         }).createDelegate(this, [i, bullet], true));
17285                 
17286         
17287     },
17288      
17289     setActiveBullet : function(i)
17290     {
17291         if(Roo.isTouch){
17292             return;
17293         }
17294         
17295         Roo.each(this.el.select('.bullet', true).elements, function(el){
17296             el.removeClass('selected');
17297         });
17298
17299         var bullet = this.el.select('.bullet-' + i, true).first();
17300         
17301         if(!bullet){
17302             return;
17303         }
17304         
17305         bullet.addClass('selected');
17306     }
17307     
17308     
17309   
17310 });
17311
17312  
17313
17314  
17315  
17316 Roo.apply(Roo.bootstrap.TabGroup, {
17317     
17318     groups: {},
17319      /**
17320     * register a Navigation Group
17321     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17322     */
17323     register : function(navgrp)
17324     {
17325         this.groups[navgrp.navId] = navgrp;
17326         
17327     },
17328     /**
17329     * fetch a Navigation Group based on the navigation ID
17330     * if one does not exist , it will get created.
17331     * @param {string} the navgroup to add
17332     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17333     */
17334     get: function(navId) {
17335         if (typeof(this.groups[navId]) == 'undefined') {
17336             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17337         }
17338         return this.groups[navId] ;
17339     }
17340     
17341     
17342     
17343 });
17344
17345  /*
17346  * - LGPL
17347  *
17348  * TabPanel
17349  * 
17350  */
17351
17352 /**
17353  * @class Roo.bootstrap.TabPanel
17354  * @extends Roo.bootstrap.Component
17355  * Bootstrap TabPanel class
17356  * @cfg {Boolean} active panel active
17357  * @cfg {String} html panel content
17358  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17359  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17360  * @cfg {String} href click to link..
17361  * 
17362  * 
17363  * @constructor
17364  * Create a new TabPanel
17365  * @param {Object} config The config object
17366  */
17367
17368 Roo.bootstrap.TabPanel = function(config){
17369     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17370     this.addEvents({
17371         /**
17372              * @event changed
17373              * Fires when the active status changes
17374              * @param {Roo.bootstrap.TabPanel} this
17375              * @param {Boolean} state the new state
17376             
17377          */
17378         'changed': true,
17379         /**
17380              * @event beforedeactivate
17381              * Fires before a tab is de-activated - can be used to do validation on a form.
17382              * @param {Roo.bootstrap.TabPanel} this
17383              * @return {Boolean} false if there is an error
17384             
17385          */
17386         'beforedeactivate': true
17387      });
17388     
17389     this.tabId = this.tabId || Roo.id();
17390   
17391 };
17392
17393 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17394     
17395     active: false,
17396     html: false,
17397     tabId: false,
17398     navId : false,
17399     href : '',
17400     
17401     getAutoCreate : function(){
17402         var cfg = {
17403             tag: 'div',
17404             // item is needed for carousel - not sure if it has any effect otherwise
17405             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17406             html: this.html || ''
17407         };
17408         
17409         if(this.active){
17410             cfg.cls += ' active';
17411         }
17412         
17413         if(this.tabId){
17414             cfg.tabId = this.tabId;
17415         }
17416         
17417         
17418         return cfg;
17419     },
17420     
17421     initEvents:  function()
17422     {
17423         var p = this.parent();
17424         
17425         this.navId = this.navId || p.navId;
17426         
17427         if (typeof(this.navId) != 'undefined') {
17428             // not really needed.. but just in case.. parent should be a NavGroup.
17429             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17430             
17431             tg.register(this);
17432             
17433             var i = tg.tabs.length - 1;
17434             
17435             if(this.active && tg.bullets > 0 && i < tg.bullets){
17436                 tg.setActiveBullet(i);
17437             }
17438         }
17439         
17440         this.el.on('click', this.onClick, this);
17441         
17442         if(Roo.isTouch){
17443             this.el.on("touchstart", this.onTouchStart, this);
17444             this.el.on("touchmove", this.onTouchMove, this);
17445             this.el.on("touchend", this.onTouchEnd, this);
17446         }
17447         
17448     },
17449     
17450     onRender : function(ct, position)
17451     {
17452         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17453     },
17454     
17455     setActive : function(state)
17456     {
17457         Roo.log("panel - set active " + this.tabId + "=" + state);
17458         
17459         this.active = state;
17460         if (!state) {
17461             this.el.removeClass('active');
17462             
17463         } else  if (!this.el.hasClass('active')) {
17464             this.el.addClass('active');
17465         }
17466         
17467         this.fireEvent('changed', this, state);
17468     },
17469     
17470     onClick : function(e)
17471     {
17472         e.preventDefault();
17473         
17474         if(!this.href.length){
17475             return;
17476         }
17477         
17478         window.location.href = this.href;
17479     },
17480     
17481     startX : 0,
17482     startY : 0,
17483     endX : 0,
17484     endY : 0,
17485     swiping : false,
17486     
17487     onTouchStart : function(e)
17488     {
17489         this.swiping = false;
17490         
17491         this.startX = e.browserEvent.touches[0].clientX;
17492         this.startY = e.browserEvent.touches[0].clientY;
17493     },
17494     
17495     onTouchMove : function(e)
17496     {
17497         this.swiping = true;
17498         
17499         this.endX = e.browserEvent.touches[0].clientX;
17500         this.endY = e.browserEvent.touches[0].clientY;
17501     },
17502     
17503     onTouchEnd : function(e)
17504     {
17505         if(!this.swiping){
17506             this.onClick(e);
17507             return;
17508         }
17509         
17510         var tabGroup = this.parent();
17511         
17512         if(this.endX > this.startX){ // swiping right
17513             tabGroup.showPanelPrev();
17514             return;
17515         }
17516         
17517         if(this.startX > this.endX){ // swiping left
17518             tabGroup.showPanelNext();
17519             return;
17520         }
17521     }
17522     
17523     
17524 });
17525  
17526
17527  
17528
17529  /*
17530  * - LGPL
17531  *
17532  * DateField
17533  * 
17534  */
17535
17536 /**
17537  * @class Roo.bootstrap.DateField
17538  * @extends Roo.bootstrap.Input
17539  * Bootstrap DateField class
17540  * @cfg {Number} weekStart default 0
17541  * @cfg {String} viewMode default empty, (months|years)
17542  * @cfg {String} minViewMode default empty, (months|years)
17543  * @cfg {Number} startDate default -Infinity
17544  * @cfg {Number} endDate default Infinity
17545  * @cfg {Boolean} todayHighlight default false
17546  * @cfg {Boolean} todayBtn default false
17547  * @cfg {Boolean} calendarWeeks default false
17548  * @cfg {Object} daysOfWeekDisabled default empty
17549  * @cfg {Boolean} singleMode default false (true | false)
17550  * 
17551  * @cfg {Boolean} keyboardNavigation default true
17552  * @cfg {String} language default en
17553  * 
17554  * @constructor
17555  * Create a new DateField
17556  * @param {Object} config The config object
17557  */
17558
17559 Roo.bootstrap.DateField = function(config){
17560     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17561      this.addEvents({
17562             /**
17563              * @event show
17564              * Fires when this field show.
17565              * @param {Roo.bootstrap.DateField} this
17566              * @param {Mixed} date The date value
17567              */
17568             show : true,
17569             /**
17570              * @event show
17571              * Fires when this field hide.
17572              * @param {Roo.bootstrap.DateField} this
17573              * @param {Mixed} date The date value
17574              */
17575             hide : true,
17576             /**
17577              * @event select
17578              * Fires when select a date.
17579              * @param {Roo.bootstrap.DateField} this
17580              * @param {Mixed} date The date value
17581              */
17582             select : true,
17583             /**
17584              * @event beforeselect
17585              * Fires when before select a date.
17586              * @param {Roo.bootstrap.DateField} this
17587              * @param {Mixed} date The date value
17588              */
17589             beforeselect : true
17590         });
17591 };
17592
17593 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17594     
17595     /**
17596      * @cfg {String} format
17597      * The default date format string which can be overriden for localization support.  The format must be
17598      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17599      */
17600     format : "m/d/y",
17601     /**
17602      * @cfg {String} altFormats
17603      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17604      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17605      */
17606     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17607     
17608     weekStart : 0,
17609     
17610     viewMode : '',
17611     
17612     minViewMode : '',
17613     
17614     todayHighlight : false,
17615     
17616     todayBtn: false,
17617     
17618     language: 'en',
17619     
17620     keyboardNavigation: true,
17621     
17622     calendarWeeks: false,
17623     
17624     startDate: -Infinity,
17625     
17626     endDate: Infinity,
17627     
17628     daysOfWeekDisabled: [],
17629     
17630     _events: [],
17631     
17632     singleMode : false,
17633     
17634     UTCDate: function()
17635     {
17636         return new Date(Date.UTC.apply(Date, arguments));
17637     },
17638     
17639     UTCToday: function()
17640     {
17641         var today = new Date();
17642         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17643     },
17644     
17645     getDate: function() {
17646             var d = this.getUTCDate();
17647             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17648     },
17649     
17650     getUTCDate: function() {
17651             return this.date;
17652     },
17653     
17654     setDate: function(d) {
17655             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17656     },
17657     
17658     setUTCDate: function(d) {
17659             this.date = d;
17660             this.setValue(this.formatDate(this.date));
17661     },
17662         
17663     onRender: function(ct, position)
17664     {
17665         
17666         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17667         
17668         this.language = this.language || 'en';
17669         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17670         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17671         
17672         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17673         this.format = this.format || 'm/d/y';
17674         this.isInline = false;
17675         this.isInput = true;
17676         this.component = this.el.select('.add-on', true).first() || false;
17677         this.component = (this.component && this.component.length === 0) ? false : this.component;
17678         this.hasInput = this.component && this.inputEl().length;
17679         
17680         if (typeof(this.minViewMode === 'string')) {
17681             switch (this.minViewMode) {
17682                 case 'months':
17683                     this.minViewMode = 1;
17684                     break;
17685                 case 'years':
17686                     this.minViewMode = 2;
17687                     break;
17688                 default:
17689                     this.minViewMode = 0;
17690                     break;
17691             }
17692         }
17693         
17694         if (typeof(this.viewMode === 'string')) {
17695             switch (this.viewMode) {
17696                 case 'months':
17697                     this.viewMode = 1;
17698                     break;
17699                 case 'years':
17700                     this.viewMode = 2;
17701                     break;
17702                 default:
17703                     this.viewMode = 0;
17704                     break;
17705             }
17706         }
17707                 
17708         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17709         
17710 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17711         
17712         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17713         
17714         this.picker().on('mousedown', this.onMousedown, this);
17715         this.picker().on('click', this.onClick, this);
17716         
17717         this.picker().addClass('datepicker-dropdown');
17718         
17719         this.startViewMode = this.viewMode;
17720         
17721         if(this.singleMode){
17722             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17723                 v.setVisibilityMode(Roo.Element.DISPLAY);
17724                 v.hide();
17725             });
17726             
17727             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17728                 v.setStyle('width', '189px');
17729             });
17730         }
17731         
17732         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17733             if(!this.calendarWeeks){
17734                 v.remove();
17735                 return;
17736             }
17737             
17738             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17739             v.attr('colspan', function(i, val){
17740                 return parseInt(val) + 1;
17741             });
17742         });
17743                         
17744         
17745         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17746         
17747         this.setStartDate(this.startDate);
17748         this.setEndDate(this.endDate);
17749         
17750         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17751         
17752         this.fillDow();
17753         this.fillMonths();
17754         this.update();
17755         this.showMode();
17756         
17757         if(this.isInline) {
17758             this.show();
17759         }
17760     },
17761     
17762     picker : function()
17763     {
17764         return this.pickerEl;
17765 //        return this.el.select('.datepicker', true).first();
17766     },
17767     
17768     fillDow: function()
17769     {
17770         var dowCnt = this.weekStart;
17771         
17772         var dow = {
17773             tag: 'tr',
17774             cn: [
17775                 
17776             ]
17777         };
17778         
17779         if(this.calendarWeeks){
17780             dow.cn.push({
17781                 tag: 'th',
17782                 cls: 'cw',
17783                 html: '&nbsp;'
17784             })
17785         }
17786         
17787         while (dowCnt < this.weekStart + 7) {
17788             dow.cn.push({
17789                 tag: 'th',
17790                 cls: 'dow',
17791                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17792             });
17793         }
17794         
17795         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17796     },
17797     
17798     fillMonths: function()
17799     {    
17800         var i = 0;
17801         var months = this.picker().select('>.datepicker-months td', true).first();
17802         
17803         months.dom.innerHTML = '';
17804         
17805         while (i < 12) {
17806             var month = {
17807                 tag: 'span',
17808                 cls: 'month',
17809                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17810             };
17811             
17812             months.createChild(month);
17813         }
17814         
17815     },
17816     
17817     update: function()
17818     {
17819         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;
17820         
17821         if (this.date < this.startDate) {
17822             this.viewDate = new Date(this.startDate);
17823         } else if (this.date > this.endDate) {
17824             this.viewDate = new Date(this.endDate);
17825         } else {
17826             this.viewDate = new Date(this.date);
17827         }
17828         
17829         this.fill();
17830     },
17831     
17832     fill: function() 
17833     {
17834         var d = new Date(this.viewDate),
17835                 year = d.getUTCFullYear(),
17836                 month = d.getUTCMonth(),
17837                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17838                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17839                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17840                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17841                 currentDate = this.date && this.date.valueOf(),
17842                 today = this.UTCToday();
17843         
17844         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17845         
17846 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17847         
17848 //        this.picker.select('>tfoot th.today').
17849 //                                              .text(dates[this.language].today)
17850 //                                              .toggle(this.todayBtn !== false);
17851     
17852         this.updateNavArrows();
17853         this.fillMonths();
17854                                                 
17855         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17856         
17857         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17858          
17859         prevMonth.setUTCDate(day);
17860         
17861         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17862         
17863         var nextMonth = new Date(prevMonth);
17864         
17865         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17866         
17867         nextMonth = nextMonth.valueOf();
17868         
17869         var fillMonths = false;
17870         
17871         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17872         
17873         while(prevMonth.valueOf() < nextMonth) {
17874             var clsName = '';
17875             
17876             if (prevMonth.getUTCDay() === this.weekStart) {
17877                 if(fillMonths){
17878                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17879                 }
17880                     
17881                 fillMonths = {
17882                     tag: 'tr',
17883                     cn: []
17884                 };
17885                 
17886                 if(this.calendarWeeks){
17887                     // ISO 8601: First week contains first thursday.
17888                     // ISO also states week starts on Monday, but we can be more abstract here.
17889                     var
17890                     // Start of current week: based on weekstart/current date
17891                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17892                     // Thursday of this week
17893                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17894                     // First Thursday of year, year from thursday
17895                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17896                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17897                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17898                     
17899                     fillMonths.cn.push({
17900                         tag: 'td',
17901                         cls: 'cw',
17902                         html: calWeek
17903                     });
17904                 }
17905             }
17906             
17907             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17908                 clsName += ' old';
17909             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17910                 clsName += ' new';
17911             }
17912             if (this.todayHighlight &&
17913                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17914                 prevMonth.getUTCMonth() == today.getMonth() &&
17915                 prevMonth.getUTCDate() == today.getDate()) {
17916                 clsName += ' today';
17917             }
17918             
17919             if (currentDate && prevMonth.valueOf() === currentDate) {
17920                 clsName += ' active';
17921             }
17922             
17923             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17924                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17925                     clsName += ' disabled';
17926             }
17927             
17928             fillMonths.cn.push({
17929                 tag: 'td',
17930                 cls: 'day ' + clsName,
17931                 html: prevMonth.getDate()
17932             });
17933             
17934             prevMonth.setDate(prevMonth.getDate()+1);
17935         }
17936           
17937         var currentYear = this.date && this.date.getUTCFullYear();
17938         var currentMonth = this.date && this.date.getUTCMonth();
17939         
17940         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17941         
17942         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17943             v.removeClass('active');
17944             
17945             if(currentYear === year && k === currentMonth){
17946                 v.addClass('active');
17947             }
17948             
17949             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17950                 v.addClass('disabled');
17951             }
17952             
17953         });
17954         
17955         
17956         year = parseInt(year/10, 10) * 10;
17957         
17958         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17959         
17960         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17961         
17962         year -= 1;
17963         for (var i = -1; i < 11; i++) {
17964             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17965                 tag: 'span',
17966                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17967                 html: year
17968             });
17969             
17970             year += 1;
17971         }
17972     },
17973     
17974     showMode: function(dir) 
17975     {
17976         if (dir) {
17977             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17978         }
17979         
17980         Roo.each(this.picker().select('>div',true).elements, function(v){
17981             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17982             v.hide();
17983         });
17984         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17985     },
17986     
17987     place: function()
17988     {
17989         if(this.isInline) {
17990             return;
17991         }
17992         
17993         this.picker().removeClass(['bottom', 'top']);
17994         
17995         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17996             /*
17997              * place to the top of element!
17998              *
17999              */
18000             
18001             this.picker().addClass('top');
18002             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18003             
18004             return;
18005         }
18006         
18007         this.picker().addClass('bottom');
18008         
18009         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18010     },
18011     
18012     parseDate : function(value)
18013     {
18014         if(!value || value instanceof Date){
18015             return value;
18016         }
18017         var v = Date.parseDate(value, this.format);
18018         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18019             v = Date.parseDate(value, 'Y-m-d');
18020         }
18021         if(!v && this.altFormats){
18022             if(!this.altFormatsArray){
18023                 this.altFormatsArray = this.altFormats.split("|");
18024             }
18025             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18026                 v = Date.parseDate(value, this.altFormatsArray[i]);
18027             }
18028         }
18029         return v;
18030     },
18031     
18032     formatDate : function(date, fmt)
18033     {   
18034         return (!date || !(date instanceof Date)) ?
18035         date : date.dateFormat(fmt || this.format);
18036     },
18037     
18038     onFocus : function()
18039     {
18040         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18041         this.show();
18042     },
18043     
18044     onBlur : function()
18045     {
18046         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18047         
18048         var d = this.inputEl().getValue();
18049         
18050         this.setValue(d);
18051                 
18052         this.hide();
18053     },
18054     
18055     show : function()
18056     {
18057         this.picker().show();
18058         this.update();
18059         this.place();
18060         
18061         this.fireEvent('show', this, this.date);
18062     },
18063     
18064     hide : function()
18065     {
18066         if(this.isInline) {
18067             return;
18068         }
18069         this.picker().hide();
18070         this.viewMode = this.startViewMode;
18071         this.showMode();
18072         
18073         this.fireEvent('hide', this, this.date);
18074         
18075     },
18076     
18077     onMousedown: function(e)
18078     {
18079         e.stopPropagation();
18080         e.preventDefault();
18081     },
18082     
18083     keyup: function(e)
18084     {
18085         Roo.bootstrap.DateField.superclass.keyup.call(this);
18086         this.update();
18087     },
18088
18089     setValue: function(v)
18090     {
18091         if(this.fireEvent('beforeselect', this, v) !== false){
18092             var d = new Date(this.parseDate(v) ).clearTime();
18093         
18094             if(isNaN(d.getTime())){
18095                 this.date = this.viewDate = '';
18096                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18097                 return;
18098             }
18099
18100             v = this.formatDate(d);
18101
18102             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18103
18104             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18105
18106             this.update();
18107
18108             this.fireEvent('select', this, this.date);
18109         }
18110     },
18111     
18112     getValue: function()
18113     {
18114         return this.formatDate(this.date);
18115     },
18116     
18117     fireKey: function(e)
18118     {
18119         if (!this.picker().isVisible()){
18120             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18121                 this.show();
18122             }
18123             return;
18124         }
18125         
18126         var dateChanged = false,
18127         dir, day, month,
18128         newDate, newViewDate;
18129         
18130         switch(e.keyCode){
18131             case 27: // escape
18132                 this.hide();
18133                 e.preventDefault();
18134                 break;
18135             case 37: // left
18136             case 39: // right
18137                 if (!this.keyboardNavigation) {
18138                     break;
18139                 }
18140                 dir = e.keyCode == 37 ? -1 : 1;
18141                 
18142                 if (e.ctrlKey){
18143                     newDate = this.moveYear(this.date, dir);
18144                     newViewDate = this.moveYear(this.viewDate, dir);
18145                 } else if (e.shiftKey){
18146                     newDate = this.moveMonth(this.date, dir);
18147                     newViewDate = this.moveMonth(this.viewDate, dir);
18148                 } else {
18149                     newDate = new Date(this.date);
18150                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18151                     newViewDate = new Date(this.viewDate);
18152                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18153                 }
18154                 if (this.dateWithinRange(newDate)){
18155                     this.date = newDate;
18156                     this.viewDate = newViewDate;
18157                     this.setValue(this.formatDate(this.date));
18158 //                    this.update();
18159                     e.preventDefault();
18160                     dateChanged = true;
18161                 }
18162                 break;
18163             case 38: // up
18164             case 40: // down
18165                 if (!this.keyboardNavigation) {
18166                     break;
18167                 }
18168                 dir = e.keyCode == 38 ? -1 : 1;
18169                 if (e.ctrlKey){
18170                     newDate = this.moveYear(this.date, dir);
18171                     newViewDate = this.moveYear(this.viewDate, dir);
18172                 } else if (e.shiftKey){
18173                     newDate = this.moveMonth(this.date, dir);
18174                     newViewDate = this.moveMonth(this.viewDate, dir);
18175                 } else {
18176                     newDate = new Date(this.date);
18177                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18178                     newViewDate = new Date(this.viewDate);
18179                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18180                 }
18181                 if (this.dateWithinRange(newDate)){
18182                     this.date = newDate;
18183                     this.viewDate = newViewDate;
18184                     this.setValue(this.formatDate(this.date));
18185 //                    this.update();
18186                     e.preventDefault();
18187                     dateChanged = true;
18188                 }
18189                 break;
18190             case 13: // enter
18191                 this.setValue(this.formatDate(this.date));
18192                 this.hide();
18193                 e.preventDefault();
18194                 break;
18195             case 9: // tab
18196                 this.setValue(this.formatDate(this.date));
18197                 this.hide();
18198                 break;
18199             case 16: // shift
18200             case 17: // ctrl
18201             case 18: // alt
18202                 break;
18203             default :
18204                 this.hide();
18205                 
18206         }
18207     },
18208     
18209     
18210     onClick: function(e) 
18211     {
18212         e.stopPropagation();
18213         e.preventDefault();
18214         
18215         var target = e.getTarget();
18216         
18217         if(target.nodeName.toLowerCase() === 'i'){
18218             target = Roo.get(target).dom.parentNode;
18219         }
18220         
18221         var nodeName = target.nodeName;
18222         var className = target.className;
18223         var html = target.innerHTML;
18224         //Roo.log(nodeName);
18225         
18226         switch(nodeName.toLowerCase()) {
18227             case 'th':
18228                 switch(className) {
18229                     case 'switch':
18230                         this.showMode(1);
18231                         break;
18232                     case 'prev':
18233                     case 'next':
18234                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18235                         switch(this.viewMode){
18236                                 case 0:
18237                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18238                                         break;
18239                                 case 1:
18240                                 case 2:
18241                                         this.viewDate = this.moveYear(this.viewDate, dir);
18242                                         break;
18243                         }
18244                         this.fill();
18245                         break;
18246                     case 'today':
18247                         var date = new Date();
18248                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18249 //                        this.fill()
18250                         this.setValue(this.formatDate(this.date));
18251                         
18252                         this.hide();
18253                         break;
18254                 }
18255                 break;
18256             case 'span':
18257                 if (className.indexOf('disabled') < 0) {
18258                     this.viewDate.setUTCDate(1);
18259                     if (className.indexOf('month') > -1) {
18260                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18261                     } else {
18262                         var year = parseInt(html, 10) || 0;
18263                         this.viewDate.setUTCFullYear(year);
18264                         
18265                     }
18266                     
18267                     if(this.singleMode){
18268                         this.setValue(this.formatDate(this.viewDate));
18269                         this.hide();
18270                         return;
18271                     }
18272                     
18273                     this.showMode(-1);
18274                     this.fill();
18275                 }
18276                 break;
18277                 
18278             case 'td':
18279                 //Roo.log(className);
18280                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18281                     var day = parseInt(html, 10) || 1;
18282                     var year = this.viewDate.getUTCFullYear(),
18283                         month = this.viewDate.getUTCMonth();
18284
18285                     if (className.indexOf('old') > -1) {
18286                         if(month === 0 ){
18287                             month = 11;
18288                             year -= 1;
18289                         }else{
18290                             month -= 1;
18291                         }
18292                     } else if (className.indexOf('new') > -1) {
18293                         if (month == 11) {
18294                             month = 0;
18295                             year += 1;
18296                         } else {
18297                             month += 1;
18298                         }
18299                     }
18300                     //Roo.log([year,month,day]);
18301                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18302                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18303 //                    this.fill();
18304                     //Roo.log(this.formatDate(this.date));
18305                     this.setValue(this.formatDate(this.date));
18306                     this.hide();
18307                 }
18308                 break;
18309         }
18310     },
18311     
18312     setStartDate: function(startDate)
18313     {
18314         this.startDate = startDate || -Infinity;
18315         if (this.startDate !== -Infinity) {
18316             this.startDate = this.parseDate(this.startDate);
18317         }
18318         this.update();
18319         this.updateNavArrows();
18320     },
18321
18322     setEndDate: function(endDate)
18323     {
18324         this.endDate = endDate || Infinity;
18325         if (this.endDate !== Infinity) {
18326             this.endDate = this.parseDate(this.endDate);
18327         }
18328         this.update();
18329         this.updateNavArrows();
18330     },
18331     
18332     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18333     {
18334         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18335         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18336             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18337         }
18338         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18339             return parseInt(d, 10);
18340         });
18341         this.update();
18342         this.updateNavArrows();
18343     },
18344     
18345     updateNavArrows: function() 
18346     {
18347         if(this.singleMode){
18348             return;
18349         }
18350         
18351         var d = new Date(this.viewDate),
18352         year = d.getUTCFullYear(),
18353         month = d.getUTCMonth();
18354         
18355         Roo.each(this.picker().select('.prev', true).elements, function(v){
18356             v.show();
18357             switch (this.viewMode) {
18358                 case 0:
18359
18360                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18361                         v.hide();
18362                     }
18363                     break;
18364                 case 1:
18365                 case 2:
18366                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18367                         v.hide();
18368                     }
18369                     break;
18370             }
18371         });
18372         
18373         Roo.each(this.picker().select('.next', true).elements, function(v){
18374             v.show();
18375             switch (this.viewMode) {
18376                 case 0:
18377
18378                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18379                         v.hide();
18380                     }
18381                     break;
18382                 case 1:
18383                 case 2:
18384                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18385                         v.hide();
18386                     }
18387                     break;
18388             }
18389         })
18390     },
18391     
18392     moveMonth: function(date, dir)
18393     {
18394         if (!dir) {
18395             return date;
18396         }
18397         var new_date = new Date(date.valueOf()),
18398         day = new_date.getUTCDate(),
18399         month = new_date.getUTCMonth(),
18400         mag = Math.abs(dir),
18401         new_month, test;
18402         dir = dir > 0 ? 1 : -1;
18403         if (mag == 1){
18404             test = dir == -1
18405             // If going back one month, make sure month is not current month
18406             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18407             ? function(){
18408                 return new_date.getUTCMonth() == month;
18409             }
18410             // If going forward one month, make sure month is as expected
18411             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18412             : function(){
18413                 return new_date.getUTCMonth() != new_month;
18414             };
18415             new_month = month + dir;
18416             new_date.setUTCMonth(new_month);
18417             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18418             if (new_month < 0 || new_month > 11) {
18419                 new_month = (new_month + 12) % 12;
18420             }
18421         } else {
18422             // For magnitudes >1, move one month at a time...
18423             for (var i=0; i<mag; i++) {
18424                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18425                 new_date = this.moveMonth(new_date, dir);
18426             }
18427             // ...then reset the day, keeping it in the new month
18428             new_month = new_date.getUTCMonth();
18429             new_date.setUTCDate(day);
18430             test = function(){
18431                 return new_month != new_date.getUTCMonth();
18432             };
18433         }
18434         // Common date-resetting loop -- if date is beyond end of month, make it
18435         // end of month
18436         while (test()){
18437             new_date.setUTCDate(--day);
18438             new_date.setUTCMonth(new_month);
18439         }
18440         return new_date;
18441     },
18442
18443     moveYear: function(date, dir)
18444     {
18445         return this.moveMonth(date, dir*12);
18446     },
18447
18448     dateWithinRange: function(date)
18449     {
18450         return date >= this.startDate && date <= this.endDate;
18451     },
18452
18453     
18454     remove: function() 
18455     {
18456         this.picker().remove();
18457     },
18458     
18459     validateValue : function(value)
18460     {
18461         if(value.length < 1)  {
18462             if(this.allowBlank){
18463                 return true;
18464             }
18465             return false;
18466         }
18467         
18468         if(value.length < this.minLength){
18469             return false;
18470         }
18471         if(value.length > this.maxLength){
18472             return false;
18473         }
18474         if(this.vtype){
18475             var vt = Roo.form.VTypes;
18476             if(!vt[this.vtype](value, this)){
18477                 return false;
18478             }
18479         }
18480         if(typeof this.validator == "function"){
18481             var msg = this.validator(value);
18482             if(msg !== true){
18483                 return false;
18484             }
18485         }
18486         
18487         if(this.regex && !this.regex.test(value)){
18488             return false;
18489         }
18490         
18491         if(typeof(this.parseDate(value)) == 'undefined'){
18492             return false;
18493         }
18494         
18495         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18496             return false;
18497         }      
18498         
18499         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18500             return false;
18501         } 
18502         
18503         
18504         return true;
18505     }
18506    
18507 });
18508
18509 Roo.apply(Roo.bootstrap.DateField,  {
18510     
18511     head : {
18512         tag: 'thead',
18513         cn: [
18514         {
18515             tag: 'tr',
18516             cn: [
18517             {
18518                 tag: 'th',
18519                 cls: 'prev',
18520                 html: '<i class="fa fa-arrow-left"/>'
18521             },
18522             {
18523                 tag: 'th',
18524                 cls: 'switch',
18525                 colspan: '5'
18526             },
18527             {
18528                 tag: 'th',
18529                 cls: 'next',
18530                 html: '<i class="fa fa-arrow-right"/>'
18531             }
18532
18533             ]
18534         }
18535         ]
18536     },
18537     
18538     content : {
18539         tag: 'tbody',
18540         cn: [
18541         {
18542             tag: 'tr',
18543             cn: [
18544             {
18545                 tag: 'td',
18546                 colspan: '7'
18547             }
18548             ]
18549         }
18550         ]
18551     },
18552     
18553     footer : {
18554         tag: 'tfoot',
18555         cn: [
18556         {
18557             tag: 'tr',
18558             cn: [
18559             {
18560                 tag: 'th',
18561                 colspan: '7',
18562                 cls: 'today'
18563             }
18564                     
18565             ]
18566         }
18567         ]
18568     },
18569     
18570     dates:{
18571         en: {
18572             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18573             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18574             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18575             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18576             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18577             today: "Today"
18578         }
18579     },
18580     
18581     modes: [
18582     {
18583         clsName: 'days',
18584         navFnc: 'Month',
18585         navStep: 1
18586     },
18587     {
18588         clsName: 'months',
18589         navFnc: 'FullYear',
18590         navStep: 1
18591     },
18592     {
18593         clsName: 'years',
18594         navFnc: 'FullYear',
18595         navStep: 10
18596     }]
18597 });
18598
18599 Roo.apply(Roo.bootstrap.DateField,  {
18600   
18601     template : {
18602         tag: 'div',
18603         cls: 'datepicker dropdown-menu roo-dynamic',
18604         cn: [
18605         {
18606             tag: 'div',
18607             cls: 'datepicker-days',
18608             cn: [
18609             {
18610                 tag: 'table',
18611                 cls: 'table-condensed',
18612                 cn:[
18613                 Roo.bootstrap.DateField.head,
18614                 {
18615                     tag: 'tbody'
18616                 },
18617                 Roo.bootstrap.DateField.footer
18618                 ]
18619             }
18620             ]
18621         },
18622         {
18623             tag: 'div',
18624             cls: 'datepicker-months',
18625             cn: [
18626             {
18627                 tag: 'table',
18628                 cls: 'table-condensed',
18629                 cn:[
18630                 Roo.bootstrap.DateField.head,
18631                 Roo.bootstrap.DateField.content,
18632                 Roo.bootstrap.DateField.footer
18633                 ]
18634             }
18635             ]
18636         },
18637         {
18638             tag: 'div',
18639             cls: 'datepicker-years',
18640             cn: [
18641             {
18642                 tag: 'table',
18643                 cls: 'table-condensed',
18644                 cn:[
18645                 Roo.bootstrap.DateField.head,
18646                 Roo.bootstrap.DateField.content,
18647                 Roo.bootstrap.DateField.footer
18648                 ]
18649             }
18650             ]
18651         }
18652         ]
18653     }
18654 });
18655
18656  
18657
18658  /*
18659  * - LGPL
18660  *
18661  * TimeField
18662  * 
18663  */
18664
18665 /**
18666  * @class Roo.bootstrap.TimeField
18667  * @extends Roo.bootstrap.Input
18668  * Bootstrap DateField class
18669  * 
18670  * 
18671  * @constructor
18672  * Create a new TimeField
18673  * @param {Object} config The config object
18674  */
18675
18676 Roo.bootstrap.TimeField = function(config){
18677     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18678     this.addEvents({
18679             /**
18680              * @event show
18681              * Fires when this field show.
18682              * @param {Roo.bootstrap.DateField} thisthis
18683              * @param {Mixed} date The date value
18684              */
18685             show : true,
18686             /**
18687              * @event show
18688              * Fires when this field hide.
18689              * @param {Roo.bootstrap.DateField} this
18690              * @param {Mixed} date The date value
18691              */
18692             hide : true,
18693             /**
18694              * @event select
18695              * Fires when select a date.
18696              * @param {Roo.bootstrap.DateField} this
18697              * @param {Mixed} date The date value
18698              */
18699             select : true
18700         });
18701 };
18702
18703 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18704     
18705     /**
18706      * @cfg {String} format
18707      * The default time format string which can be overriden for localization support.  The format must be
18708      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18709      */
18710     format : "H:i",
18711        
18712     onRender: function(ct, position)
18713     {
18714         
18715         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18716                 
18717         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18718         
18719         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18720         
18721         this.pop = this.picker().select('>.datepicker-time',true).first();
18722         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18723         
18724         this.picker().on('mousedown', this.onMousedown, this);
18725         this.picker().on('click', this.onClick, this);
18726         
18727         this.picker().addClass('datepicker-dropdown');
18728     
18729         this.fillTime();
18730         this.update();
18731             
18732         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18733         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18734         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18735         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18736         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18737         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18738
18739     },
18740     
18741     fireKey: function(e){
18742         if (!this.picker().isVisible()){
18743             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18744                 this.show();
18745             }
18746             return;
18747         }
18748
18749         e.preventDefault();
18750         
18751         switch(e.keyCode){
18752             case 27: // escape
18753                 this.hide();
18754                 break;
18755             case 37: // left
18756             case 39: // right
18757                 this.onTogglePeriod();
18758                 break;
18759             case 38: // up
18760                 this.onIncrementMinutes();
18761                 break;
18762             case 40: // down
18763                 this.onDecrementMinutes();
18764                 break;
18765             case 13: // enter
18766             case 9: // tab
18767                 this.setTime();
18768                 break;
18769         }
18770     },
18771     
18772     onClick: function(e) {
18773         e.stopPropagation();
18774         e.preventDefault();
18775     },
18776     
18777     picker : function()
18778     {
18779         return this.el.select('.datepicker', true).first();
18780     },
18781     
18782     fillTime: function()
18783     {    
18784         var time = this.pop.select('tbody', true).first();
18785         
18786         time.dom.innerHTML = '';
18787         
18788         time.createChild({
18789             tag: 'tr',
18790             cn: [
18791                 {
18792                     tag: 'td',
18793                     cn: [
18794                         {
18795                             tag: 'a',
18796                             href: '#',
18797                             cls: 'btn',
18798                             cn: [
18799                                 {
18800                                     tag: 'span',
18801                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18802                                 }
18803                             ]
18804                         } 
18805                     ]
18806                 },
18807                 {
18808                     tag: 'td',
18809                     cls: 'separator'
18810                 },
18811                 {
18812                     tag: 'td',
18813                     cn: [
18814                         {
18815                             tag: 'a',
18816                             href: '#',
18817                             cls: 'btn',
18818                             cn: [
18819                                 {
18820                                     tag: 'span',
18821                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18822                                 }
18823                             ]
18824                         }
18825                     ]
18826                 },
18827                 {
18828                     tag: 'td',
18829                     cls: 'separator'
18830                 }
18831             ]
18832         });
18833         
18834         time.createChild({
18835             tag: 'tr',
18836             cn: [
18837                 {
18838                     tag: 'td',
18839                     cn: [
18840                         {
18841                             tag: 'span',
18842                             cls: 'timepicker-hour',
18843                             html: '00'
18844                         }  
18845                     ]
18846                 },
18847                 {
18848                     tag: 'td',
18849                     cls: 'separator',
18850                     html: ':'
18851                 },
18852                 {
18853                     tag: 'td',
18854                     cn: [
18855                         {
18856                             tag: 'span',
18857                             cls: 'timepicker-minute',
18858                             html: '00'
18859                         }  
18860                     ]
18861                 },
18862                 {
18863                     tag: 'td',
18864                     cls: 'separator'
18865                 },
18866                 {
18867                     tag: 'td',
18868                     cn: [
18869                         {
18870                             tag: 'button',
18871                             type: 'button',
18872                             cls: 'btn btn-primary period',
18873                             html: 'AM'
18874                             
18875                         }
18876                     ]
18877                 }
18878             ]
18879         });
18880         
18881         time.createChild({
18882             tag: 'tr',
18883             cn: [
18884                 {
18885                     tag: 'td',
18886                     cn: [
18887                         {
18888                             tag: 'a',
18889                             href: '#',
18890                             cls: 'btn',
18891                             cn: [
18892                                 {
18893                                     tag: 'span',
18894                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18895                                 }
18896                             ]
18897                         }
18898                     ]
18899                 },
18900                 {
18901                     tag: 'td',
18902                     cls: 'separator'
18903                 },
18904                 {
18905                     tag: 'td',
18906                     cn: [
18907                         {
18908                             tag: 'a',
18909                             href: '#',
18910                             cls: 'btn',
18911                             cn: [
18912                                 {
18913                                     tag: 'span',
18914                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18915                                 }
18916                             ]
18917                         }
18918                     ]
18919                 },
18920                 {
18921                     tag: 'td',
18922                     cls: 'separator'
18923                 }
18924             ]
18925         });
18926         
18927     },
18928     
18929     update: function()
18930     {
18931         
18932         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18933         
18934         this.fill();
18935     },
18936     
18937     fill: function() 
18938     {
18939         var hours = this.time.getHours();
18940         var minutes = this.time.getMinutes();
18941         var period = 'AM';
18942         
18943         if(hours > 11){
18944             period = 'PM';
18945         }
18946         
18947         if(hours == 0){
18948             hours = 12;
18949         }
18950         
18951         
18952         if(hours > 12){
18953             hours = hours - 12;
18954         }
18955         
18956         if(hours < 10){
18957             hours = '0' + hours;
18958         }
18959         
18960         if(minutes < 10){
18961             minutes = '0' + minutes;
18962         }
18963         
18964         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18965         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18966         this.pop.select('button', true).first().dom.innerHTML = period;
18967         
18968     },
18969     
18970     place: function()
18971     {   
18972         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18973         
18974         var cls = ['bottom'];
18975         
18976         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18977             cls.pop();
18978             cls.push('top');
18979         }
18980         
18981         cls.push('right');
18982         
18983         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18984             cls.pop();
18985             cls.push('left');
18986         }
18987         
18988         this.picker().addClass(cls.join('-'));
18989         
18990         var _this = this;
18991         
18992         Roo.each(cls, function(c){
18993             if(c == 'bottom'){
18994                 _this.picker().setTop(_this.inputEl().getHeight());
18995                 return;
18996             }
18997             if(c == 'top'){
18998                 _this.picker().setTop(0 - _this.picker().getHeight());
18999                 return;
19000             }
19001             
19002             if(c == 'left'){
19003                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19004                 return;
19005             }
19006             if(c == 'right'){
19007                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19008                 return;
19009             }
19010         });
19011         
19012     },
19013   
19014     onFocus : function()
19015     {
19016         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19017         this.show();
19018     },
19019     
19020     onBlur : function()
19021     {
19022         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19023         this.hide();
19024     },
19025     
19026     show : function()
19027     {
19028         this.picker().show();
19029         this.pop.show();
19030         this.update();
19031         this.place();
19032         
19033         this.fireEvent('show', this, this.date);
19034     },
19035     
19036     hide : function()
19037     {
19038         this.picker().hide();
19039         this.pop.hide();
19040         
19041         this.fireEvent('hide', this, this.date);
19042     },
19043     
19044     setTime : function()
19045     {
19046         this.hide();
19047         this.setValue(this.time.format(this.format));
19048         
19049         this.fireEvent('select', this, this.date);
19050         
19051         
19052     },
19053     
19054     onMousedown: function(e){
19055         e.stopPropagation();
19056         e.preventDefault();
19057     },
19058     
19059     onIncrementHours: function()
19060     {
19061         Roo.log('onIncrementHours');
19062         this.time = this.time.add(Date.HOUR, 1);
19063         this.update();
19064         
19065     },
19066     
19067     onDecrementHours: function()
19068     {
19069         Roo.log('onDecrementHours');
19070         this.time = this.time.add(Date.HOUR, -1);
19071         this.update();
19072     },
19073     
19074     onIncrementMinutes: function()
19075     {
19076         Roo.log('onIncrementMinutes');
19077         this.time = this.time.add(Date.MINUTE, 1);
19078         this.update();
19079     },
19080     
19081     onDecrementMinutes: function()
19082     {
19083         Roo.log('onDecrementMinutes');
19084         this.time = this.time.add(Date.MINUTE, -1);
19085         this.update();
19086     },
19087     
19088     onTogglePeriod: function()
19089     {
19090         Roo.log('onTogglePeriod');
19091         this.time = this.time.add(Date.HOUR, 12);
19092         this.update();
19093     }
19094     
19095    
19096 });
19097
19098 Roo.apply(Roo.bootstrap.TimeField,  {
19099     
19100     content : {
19101         tag: 'tbody',
19102         cn: [
19103             {
19104                 tag: 'tr',
19105                 cn: [
19106                 {
19107                     tag: 'td',
19108                     colspan: '7'
19109                 }
19110                 ]
19111             }
19112         ]
19113     },
19114     
19115     footer : {
19116         tag: 'tfoot',
19117         cn: [
19118             {
19119                 tag: 'tr',
19120                 cn: [
19121                 {
19122                     tag: 'th',
19123                     colspan: '7',
19124                     cls: '',
19125                     cn: [
19126                         {
19127                             tag: 'button',
19128                             cls: 'btn btn-info ok',
19129                             html: 'OK'
19130                         }
19131                     ]
19132                 }
19133
19134                 ]
19135             }
19136         ]
19137     }
19138 });
19139
19140 Roo.apply(Roo.bootstrap.TimeField,  {
19141   
19142     template : {
19143         tag: 'div',
19144         cls: 'datepicker dropdown-menu',
19145         cn: [
19146             {
19147                 tag: 'div',
19148                 cls: 'datepicker-time',
19149                 cn: [
19150                 {
19151                     tag: 'table',
19152                     cls: 'table-condensed',
19153                     cn:[
19154                     Roo.bootstrap.TimeField.content,
19155                     Roo.bootstrap.TimeField.footer
19156                     ]
19157                 }
19158                 ]
19159             }
19160         ]
19161     }
19162 });
19163
19164  
19165
19166  /*
19167  * - LGPL
19168  *
19169  * MonthField
19170  * 
19171  */
19172
19173 /**
19174  * @class Roo.bootstrap.MonthField
19175  * @extends Roo.bootstrap.Input
19176  * Bootstrap MonthField class
19177  * 
19178  * @cfg {String} language default en
19179  * 
19180  * @constructor
19181  * Create a new MonthField
19182  * @param {Object} config The config object
19183  */
19184
19185 Roo.bootstrap.MonthField = function(config){
19186     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19187     
19188     this.addEvents({
19189         /**
19190          * @event show
19191          * Fires when this field show.
19192          * @param {Roo.bootstrap.MonthField} this
19193          * @param {Mixed} date The date value
19194          */
19195         show : true,
19196         /**
19197          * @event show
19198          * Fires when this field hide.
19199          * @param {Roo.bootstrap.MonthField} this
19200          * @param {Mixed} date The date value
19201          */
19202         hide : true,
19203         /**
19204          * @event select
19205          * Fires when select a date.
19206          * @param {Roo.bootstrap.MonthField} this
19207          * @param {String} oldvalue The old value
19208          * @param {String} newvalue The new value
19209          */
19210         select : true
19211     });
19212 };
19213
19214 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19215     
19216     onRender: function(ct, position)
19217     {
19218         
19219         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19220         
19221         this.language = this.language || 'en';
19222         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19223         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19224         
19225         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19226         this.isInline = false;
19227         this.isInput = true;
19228         this.component = this.el.select('.add-on', true).first() || false;
19229         this.component = (this.component && this.component.length === 0) ? false : this.component;
19230         this.hasInput = this.component && this.inputEL().length;
19231         
19232         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19233         
19234         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19235         
19236         this.picker().on('mousedown', this.onMousedown, this);
19237         this.picker().on('click', this.onClick, this);
19238         
19239         this.picker().addClass('datepicker-dropdown');
19240         
19241         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19242             v.setStyle('width', '189px');
19243         });
19244         
19245         this.fillMonths();
19246         
19247         this.update();
19248         
19249         if(this.isInline) {
19250             this.show();
19251         }
19252         
19253     },
19254     
19255     setValue: function(v, suppressEvent)
19256     {   
19257         var o = this.getValue();
19258         
19259         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19260         
19261         this.update();
19262
19263         if(suppressEvent !== true){
19264             this.fireEvent('select', this, o, v);
19265         }
19266         
19267     },
19268     
19269     getValue: function()
19270     {
19271         return this.value;
19272     },
19273     
19274     onClick: function(e) 
19275     {
19276         e.stopPropagation();
19277         e.preventDefault();
19278         
19279         var target = e.getTarget();
19280         
19281         if(target.nodeName.toLowerCase() === 'i'){
19282             target = Roo.get(target).dom.parentNode;
19283         }
19284         
19285         var nodeName = target.nodeName;
19286         var className = target.className;
19287         var html = target.innerHTML;
19288         
19289         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19290             return;
19291         }
19292         
19293         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19294         
19295         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19296         
19297         this.hide();
19298                         
19299     },
19300     
19301     picker : function()
19302     {
19303         return this.pickerEl;
19304     },
19305     
19306     fillMonths: function()
19307     {    
19308         var i = 0;
19309         var months = this.picker().select('>.datepicker-months td', true).first();
19310         
19311         months.dom.innerHTML = '';
19312         
19313         while (i < 12) {
19314             var month = {
19315                 tag: 'span',
19316                 cls: 'month',
19317                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19318             };
19319             
19320             months.createChild(month);
19321         }
19322         
19323     },
19324     
19325     update: function()
19326     {
19327         var _this = this;
19328         
19329         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19330             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19331         }
19332         
19333         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19334             e.removeClass('active');
19335             
19336             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19337                 e.addClass('active');
19338             }
19339         })
19340     },
19341     
19342     place: function()
19343     {
19344         if(this.isInline) {
19345             return;
19346         }
19347         
19348         this.picker().removeClass(['bottom', 'top']);
19349         
19350         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19351             /*
19352              * place to the top of element!
19353              *
19354              */
19355             
19356             this.picker().addClass('top');
19357             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19358             
19359             return;
19360         }
19361         
19362         this.picker().addClass('bottom');
19363         
19364         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19365     },
19366     
19367     onFocus : function()
19368     {
19369         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19370         this.show();
19371     },
19372     
19373     onBlur : function()
19374     {
19375         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19376         
19377         var d = this.inputEl().getValue();
19378         
19379         this.setValue(d);
19380                 
19381         this.hide();
19382     },
19383     
19384     show : function()
19385     {
19386         this.picker().show();
19387         this.picker().select('>.datepicker-months', true).first().show();
19388         this.update();
19389         this.place();
19390         
19391         this.fireEvent('show', this, this.date);
19392     },
19393     
19394     hide : function()
19395     {
19396         if(this.isInline) {
19397             return;
19398         }
19399         this.picker().hide();
19400         this.fireEvent('hide', this, this.date);
19401         
19402     },
19403     
19404     onMousedown: function(e)
19405     {
19406         e.stopPropagation();
19407         e.preventDefault();
19408     },
19409     
19410     keyup: function(e)
19411     {
19412         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19413         this.update();
19414     },
19415
19416     fireKey: function(e)
19417     {
19418         if (!this.picker().isVisible()){
19419             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19420                 this.show();
19421             }
19422             return;
19423         }
19424         
19425         var dir;
19426         
19427         switch(e.keyCode){
19428             case 27: // escape
19429                 this.hide();
19430                 e.preventDefault();
19431                 break;
19432             case 37: // left
19433             case 39: // right
19434                 dir = e.keyCode == 37 ? -1 : 1;
19435                 
19436                 this.vIndex = this.vIndex + dir;
19437                 
19438                 if(this.vIndex < 0){
19439                     this.vIndex = 0;
19440                 }
19441                 
19442                 if(this.vIndex > 11){
19443                     this.vIndex = 11;
19444                 }
19445                 
19446                 if(isNaN(this.vIndex)){
19447                     this.vIndex = 0;
19448                 }
19449                 
19450                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19451                 
19452                 break;
19453             case 38: // up
19454             case 40: // down
19455                 
19456                 dir = e.keyCode == 38 ? -1 : 1;
19457                 
19458                 this.vIndex = this.vIndex + dir * 4;
19459                 
19460                 if(this.vIndex < 0){
19461                     this.vIndex = 0;
19462                 }
19463                 
19464                 if(this.vIndex > 11){
19465                     this.vIndex = 11;
19466                 }
19467                 
19468                 if(isNaN(this.vIndex)){
19469                     this.vIndex = 0;
19470                 }
19471                 
19472                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19473                 break;
19474                 
19475             case 13: // enter
19476                 
19477                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19478                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19479                 }
19480                 
19481                 this.hide();
19482                 e.preventDefault();
19483                 break;
19484             case 9: // tab
19485                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19486                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19487                 }
19488                 this.hide();
19489                 break;
19490             case 16: // shift
19491             case 17: // ctrl
19492             case 18: // alt
19493                 break;
19494             default :
19495                 this.hide();
19496                 
19497         }
19498     },
19499     
19500     remove: function() 
19501     {
19502         this.picker().remove();
19503     }
19504    
19505 });
19506
19507 Roo.apply(Roo.bootstrap.MonthField,  {
19508     
19509     content : {
19510         tag: 'tbody',
19511         cn: [
19512         {
19513             tag: 'tr',
19514             cn: [
19515             {
19516                 tag: 'td',
19517                 colspan: '7'
19518             }
19519             ]
19520         }
19521         ]
19522     },
19523     
19524     dates:{
19525         en: {
19526             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19527             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19528         }
19529     }
19530 });
19531
19532 Roo.apply(Roo.bootstrap.MonthField,  {
19533   
19534     template : {
19535         tag: 'div',
19536         cls: 'datepicker dropdown-menu roo-dynamic',
19537         cn: [
19538             {
19539                 tag: 'div',
19540                 cls: 'datepicker-months',
19541                 cn: [
19542                 {
19543                     tag: 'table',
19544                     cls: 'table-condensed',
19545                     cn:[
19546                         Roo.bootstrap.DateField.content
19547                     ]
19548                 }
19549                 ]
19550             }
19551         ]
19552     }
19553 });
19554
19555  
19556
19557  
19558  /*
19559  * - LGPL
19560  *
19561  * CheckBox
19562  * 
19563  */
19564
19565 /**
19566  * @class Roo.bootstrap.CheckBox
19567  * @extends Roo.bootstrap.Input
19568  * Bootstrap CheckBox class
19569  * 
19570  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19571  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19572  * @cfg {String} boxLabel The text that appears beside the checkbox
19573  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19574  * @cfg {Boolean} checked initnal the element
19575  * @cfg {Boolean} inline inline the element (default false)
19576  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19577  * 
19578  * @constructor
19579  * Create a new CheckBox
19580  * @param {Object} config The config object
19581  */
19582
19583 Roo.bootstrap.CheckBox = function(config){
19584     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19585    
19586     this.addEvents({
19587         /**
19588         * @event check
19589         * Fires when the element is checked or unchecked.
19590         * @param {Roo.bootstrap.CheckBox} this This input
19591         * @param {Boolean} checked The new checked value
19592         */
19593        check : true
19594     });
19595     
19596 };
19597
19598 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19599   
19600     inputType: 'checkbox',
19601     inputValue: 1,
19602     valueOff: 0,
19603     boxLabel: false,
19604     checked: false,
19605     weight : false,
19606     inline: false,
19607     
19608     getAutoCreate : function()
19609     {
19610         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19611         
19612         var id = Roo.id();
19613         
19614         var cfg = {};
19615         
19616         cfg.cls = 'form-group ' + this.inputType; //input-group
19617         
19618         if(this.inline){
19619             cfg.cls += ' ' + this.inputType + '-inline';
19620         }
19621         
19622         var input =  {
19623             tag: 'input',
19624             id : id,
19625             type : this.inputType,
19626             value : this.inputValue,
19627             cls : 'roo-' + this.inputType, //'form-box',
19628             placeholder : this.placeholder || ''
19629             
19630         };
19631         
19632         if(this.inputType != 'radio'){
19633             var hidden =  {
19634                 tag: 'input',
19635                 type : 'hidden',
19636                 cls : 'roo-hidden-value',
19637                 value : this.checked ? this.valueOff : this.inputValue
19638             };
19639         }
19640         
19641             
19642         if (this.weight) { // Validity check?
19643             cfg.cls += " " + this.inputType + "-" + this.weight;
19644         }
19645         
19646         if (this.disabled) {
19647             input.disabled=true;
19648         }
19649         
19650         if(this.checked){
19651             input.checked = this.checked;
19652             
19653         }
19654         
19655         
19656         if (this.name) {
19657             
19658             input.name = this.name;
19659             
19660             if(this.inputType != 'radio'){
19661                 hidden.name = this.name;
19662                 input.name = '_hidden_' + this.name;
19663             }
19664         }
19665         
19666         if (this.size) {
19667             input.cls += ' input-' + this.size;
19668         }
19669         
19670         var settings=this;
19671         
19672         ['xs','sm','md','lg'].map(function(size){
19673             if (settings[size]) {
19674                 cfg.cls += ' col-' + size + '-' + settings[size];
19675             }
19676         });
19677         
19678         var inputblock = input;
19679          
19680         if (this.before || this.after) {
19681             
19682             inputblock = {
19683                 cls : 'input-group',
19684                 cn :  [] 
19685             };
19686             
19687             if (this.before) {
19688                 inputblock.cn.push({
19689                     tag :'span',
19690                     cls : 'input-group-addon',
19691                     html : this.before
19692                 });
19693             }
19694             
19695             inputblock.cn.push(input);
19696             
19697             if(this.inputType != 'radio'){
19698                 inputblock.cn.push(hidden);
19699             }
19700             
19701             if (this.after) {
19702                 inputblock.cn.push({
19703                     tag :'span',
19704                     cls : 'input-group-addon',
19705                     html : this.after
19706                 });
19707             }
19708             
19709         }
19710         
19711         if (align ==='left' && this.fieldLabel.length) {
19712 //                Roo.log("left and has label");
19713                 cfg.cn = [
19714                     
19715                     {
19716                         tag: 'label',
19717                         'for' :  id,
19718                         cls : 'control-label col-md-' + this.labelWidth,
19719                         html : this.fieldLabel
19720                         
19721                     },
19722                     {
19723                         cls : "col-md-" + (12 - this.labelWidth), 
19724                         cn: [
19725                             inputblock
19726                         ]
19727                     }
19728                     
19729                 ];
19730         } else if ( this.fieldLabel.length) {
19731 //                Roo.log(" label");
19732                 cfg.cn = [
19733                    
19734                     {
19735                         tag: this.boxLabel ? 'span' : 'label',
19736                         'for': id,
19737                         cls: 'control-label box-input-label',
19738                         //cls : 'input-group-addon',
19739                         html : this.fieldLabel
19740                         
19741                     },
19742                     
19743                     inputblock
19744                     
19745                 ];
19746
19747         } else {
19748             
19749 //                Roo.log(" no label && no align");
19750                 cfg.cn = [  inputblock ] ;
19751                 
19752                 
19753         }
19754         
19755         if(this.boxLabel){
19756              var boxLabelCfg = {
19757                 tag: 'label',
19758                 //'for': id, // box label is handled by onclick - so no for...
19759                 cls: 'box-label',
19760                 html: this.boxLabel
19761             };
19762             
19763             if(this.tooltip){
19764                 boxLabelCfg.tooltip = this.tooltip;
19765             }
19766              
19767             cfg.cn.push(boxLabelCfg);
19768         }
19769         
19770         if(this.inputType != 'radio'){
19771             cfg.cn.push(hidden);
19772         }
19773         
19774         return cfg;
19775         
19776     },
19777     
19778     /**
19779      * return the real input element.
19780      */
19781     inputEl: function ()
19782     {
19783         return this.el.select('input.roo-' + this.inputType,true).first();
19784     },
19785     hiddenEl: function ()
19786     {
19787         return this.el.select('input.roo-hidden-value',true).first();
19788     },
19789     
19790     labelEl: function()
19791     {
19792         return this.el.select('label.control-label',true).first();
19793     },
19794     /* depricated... */
19795     
19796     label: function()
19797     {
19798         return this.labelEl();
19799     },
19800     
19801     boxLabelEl: function()
19802     {
19803         return this.el.select('label.box-label',true).first();
19804     },
19805     
19806     initEvents : function()
19807     {
19808 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19809         
19810         this.inputEl().on('click', this.onClick,  this);
19811         
19812         if (this.boxLabel) { 
19813             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19814         }
19815         
19816         this.startValue = this.getValue();
19817         
19818         if(this.groupId){
19819             Roo.bootstrap.CheckBox.register(this);
19820         }
19821     },
19822     
19823     onClick : function()
19824     {   
19825         this.setChecked(!this.checked);
19826     },
19827     
19828     setChecked : function(state,suppressEvent)
19829     {
19830         this.startValue = this.getValue();
19831
19832         if(this.inputType == 'radio'){
19833             
19834             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19835                 e.dom.checked = false;
19836             });
19837             
19838             this.inputEl().dom.checked = true;
19839             
19840             this.inputEl().dom.value = this.inputValue;
19841             
19842             if(suppressEvent !== true){
19843                 this.fireEvent('check', this, true);
19844             }
19845             
19846             this.validate();
19847             
19848             return;
19849         }
19850         
19851         this.checked = state;
19852         
19853         this.inputEl().dom.checked = state;
19854         
19855         
19856         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19857         
19858         if(suppressEvent !== true){
19859             this.fireEvent('check', this, state);
19860         }
19861         
19862         this.validate();
19863     },
19864     
19865     getValue : function()
19866     {
19867         if(this.inputType == 'radio'){
19868             return this.getGroupValue();
19869         }
19870         
19871         return this.hiddenEl().dom.value;
19872         
19873     },
19874     
19875     getGroupValue : function()
19876     {
19877         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19878             return '';
19879         }
19880         
19881         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19882     },
19883     
19884     setValue : function(v,suppressEvent)
19885     {
19886         if(this.inputType == 'radio'){
19887             this.setGroupValue(v, suppressEvent);
19888             return;
19889         }
19890         
19891         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19892         
19893         this.validate();
19894     },
19895     
19896     setGroupValue : function(v, suppressEvent)
19897     {
19898         this.startValue = this.getValue();
19899         
19900         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19901             e.dom.checked = false;
19902             
19903             if(e.dom.value == v){
19904                 e.dom.checked = true;
19905             }
19906         });
19907         
19908         if(suppressEvent !== true){
19909             this.fireEvent('check', this, true);
19910         }
19911
19912         this.validate();
19913         
19914         return;
19915     },
19916     
19917     validate : function()
19918     {
19919         if(
19920                 this.disabled || 
19921                 (this.inputType == 'radio' && this.validateRadio()) ||
19922                 (this.inputType == 'checkbox' && this.validateCheckbox())
19923         ){
19924             this.markValid();
19925             return true;
19926         }
19927         
19928         this.markInvalid();
19929         return false;
19930     },
19931     
19932     validateRadio : function()
19933     {
19934         if(this.allowBlank){
19935             return true;
19936         }
19937         
19938         var valid = false;
19939         
19940         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19941             if(!e.dom.checked){
19942                 return;
19943             }
19944             
19945             valid = true;
19946             
19947             return false;
19948         });
19949         
19950         return valid;
19951     },
19952     
19953     validateCheckbox : function()
19954     {
19955         if(!this.groupId){
19956             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19957         }
19958         
19959         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19960         
19961         if(!group){
19962             return false;
19963         }
19964         
19965         var r = false;
19966         
19967         for(var i in group){
19968             if(r){
19969                 break;
19970             }
19971             
19972             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19973         }
19974         
19975         return r;
19976     },
19977     
19978     /**
19979      * Mark this field as valid
19980      */
19981     markValid : function()
19982     {
19983         var _this = this;
19984         
19985         this.fireEvent('valid', this);
19986         
19987         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19988         
19989         if(this.groupId){
19990             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19991         }
19992         
19993         if(label){
19994             label.markValid();
19995         }
19996
19997         if(this.inputType == 'radio'){
19998             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19999                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20000                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20001             });
20002             
20003             return;
20004         }
20005         
20006         if(!this.groupId){
20007             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20008             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20009             return;
20010         }
20011         
20012         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20013             
20014         if(!group){
20015             return;
20016         }
20017         
20018         for(var i in group){
20019             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20020             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20021         }
20022     },
20023     
20024      /**
20025      * Mark this field as invalid
20026      * @param {String} msg The validation message
20027      */
20028     markInvalid : function(msg)
20029     {
20030         if(this.allowBlank){
20031             return;
20032         }
20033         
20034         var _this = this;
20035         
20036         this.fireEvent('invalid', this, msg);
20037         
20038         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20039         
20040         if(this.groupId){
20041             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20042         }
20043         
20044         if(label){
20045             label.markInvalid();
20046         }
20047             
20048         if(this.inputType == 'radio'){
20049             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20050                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20051                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20052             });
20053             
20054             return;
20055         }
20056         
20057         if(!this.groupId){
20058             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20059             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20060             return;
20061         }
20062         
20063         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20064         
20065         if(!group){
20066             return;
20067         }
20068         
20069         for(var i in group){
20070             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20071             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20072         }
20073         
20074     },
20075     
20076     clearInvalid : function()
20077     {
20078         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20079         
20080         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20081         
20082         if (label) {
20083             label.iconEl.removeClass(label.validClass);
20084             label.iconEl.removeClass(label.invalidClass);
20085         }
20086     },
20087     
20088     disable : function()
20089     {
20090         if(this.inputType != 'radio'){
20091             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20092             return;
20093         }
20094         
20095         var _this = this;
20096         
20097         if(this.rendered){
20098             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20099                 _this.getActionEl().addClass(this.disabledClass);
20100                 e.dom.disabled = true;
20101             });
20102         }
20103         
20104         this.disabled = true;
20105         this.fireEvent("disable", this);
20106         return this;
20107     },
20108
20109     enable : function()
20110     {
20111         if(this.inputType != 'radio'){
20112             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20113             return;
20114         }
20115         
20116         var _this = this;
20117         
20118         if(this.rendered){
20119             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20120                 _this.getActionEl().removeClass(this.disabledClass);
20121                 e.dom.disabled = false;
20122             });
20123         }
20124         
20125         this.disabled = false;
20126         this.fireEvent("enable", this);
20127         return this;
20128     }
20129
20130 });
20131
20132 Roo.apply(Roo.bootstrap.CheckBox, {
20133     
20134     groups: {},
20135     
20136      /**
20137     * register a CheckBox Group
20138     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20139     */
20140     register : function(checkbox)
20141     {
20142         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20143             this.groups[checkbox.groupId] = {};
20144         }
20145         
20146         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20147             return;
20148         }
20149         
20150         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20151         
20152     },
20153     /**
20154     * fetch a CheckBox Group based on the group ID
20155     * @param {string} the group ID
20156     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20157     */
20158     get: function(groupId) {
20159         if (typeof(this.groups[groupId]) == 'undefined') {
20160             return false;
20161         }
20162         
20163         return this.groups[groupId] ;
20164     }
20165     
20166     
20167 });
20168 /*
20169  * - LGPL
20170  *
20171  * RadioItem
20172  * 
20173  */
20174
20175 /**
20176  * @class Roo.bootstrap.Radio
20177  * @extends Roo.bootstrap.Component
20178  * Bootstrap Radio class
20179  * @cfg {String} boxLabel - the label associated
20180  * @cfg {String} value - the value of radio
20181  * 
20182  * @constructor
20183  * Create a new Radio
20184  * @param {Object} config The config object
20185  */
20186 Roo.bootstrap.Radio = function(config){
20187     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20188     
20189 };
20190
20191 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20192     
20193     boxLabel : '',
20194     
20195     value : '',
20196     
20197     getAutoCreate : function()
20198     {
20199         var cfg = {
20200             tag : 'div',
20201             cls : 'form-group radio',
20202             cn : [
20203                 {
20204                     tag : 'label',
20205                     cls : 'box-label',
20206                     html : this.boxLabel
20207                 }
20208             ]
20209         };
20210         
20211         return cfg;
20212     },
20213     
20214     initEvents : function() 
20215     {
20216         this.parent().register(this);
20217         
20218         this.el.on('click', this.onClick, this);
20219         
20220     },
20221     
20222     onClick : function()
20223     {
20224         this.setChecked(true);
20225     },
20226     
20227     setChecked : function(state, suppressEvent)
20228     {
20229         this.parent().setValue(this.value, suppressEvent);
20230         
20231     }
20232     
20233 });
20234  
20235
20236  //<script type="text/javascript">
20237
20238 /*
20239  * Based  Ext JS Library 1.1.1
20240  * Copyright(c) 2006-2007, Ext JS, LLC.
20241  * LGPL
20242  *
20243  */
20244  
20245 /**
20246  * @class Roo.HtmlEditorCore
20247  * @extends Roo.Component
20248  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20249  *
20250  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20251  */
20252
20253 Roo.HtmlEditorCore = function(config){
20254     
20255     
20256     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20257     
20258     
20259     this.addEvents({
20260         /**
20261          * @event initialize
20262          * Fires when the editor is fully initialized (including the iframe)
20263          * @param {Roo.HtmlEditorCore} this
20264          */
20265         initialize: true,
20266         /**
20267          * @event activate
20268          * Fires when the editor is first receives the focus. Any insertion must wait
20269          * until after this event.
20270          * @param {Roo.HtmlEditorCore} this
20271          */
20272         activate: true,
20273          /**
20274          * @event beforesync
20275          * Fires before the textarea is updated with content from the editor iframe. Return false
20276          * to cancel the sync.
20277          * @param {Roo.HtmlEditorCore} this
20278          * @param {String} html
20279          */
20280         beforesync: true,
20281          /**
20282          * @event beforepush
20283          * Fires before the iframe editor is updated with content from the textarea. Return false
20284          * to cancel the push.
20285          * @param {Roo.HtmlEditorCore} this
20286          * @param {String} html
20287          */
20288         beforepush: true,
20289          /**
20290          * @event sync
20291          * Fires when the textarea is updated with content from the editor iframe.
20292          * @param {Roo.HtmlEditorCore} this
20293          * @param {String} html
20294          */
20295         sync: true,
20296          /**
20297          * @event push
20298          * Fires when the iframe editor is updated with content from the textarea.
20299          * @param {Roo.HtmlEditorCore} this
20300          * @param {String} html
20301          */
20302         push: true,
20303         
20304         /**
20305          * @event editorevent
20306          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20307          * @param {Roo.HtmlEditorCore} this
20308          */
20309         editorevent: true
20310         
20311     });
20312     
20313     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20314     
20315     // defaults : white / black...
20316     this.applyBlacklists();
20317     
20318     
20319     
20320 };
20321
20322
20323 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20324
20325
20326      /**
20327      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20328      */
20329     
20330     owner : false,
20331     
20332      /**
20333      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20334      *                        Roo.resizable.
20335      */
20336     resizable : false,
20337      /**
20338      * @cfg {Number} height (in pixels)
20339      */   
20340     height: 300,
20341    /**
20342      * @cfg {Number} width (in pixels)
20343      */   
20344     width: 500,
20345     
20346     /**
20347      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20348      * 
20349      */
20350     stylesheets: false,
20351     
20352     // id of frame..
20353     frameId: false,
20354     
20355     // private properties
20356     validationEvent : false,
20357     deferHeight: true,
20358     initialized : false,
20359     activated : false,
20360     sourceEditMode : false,
20361     onFocus : Roo.emptyFn,
20362     iframePad:3,
20363     hideMode:'offsets',
20364     
20365     clearUp: true,
20366     
20367     // blacklist + whitelisted elements..
20368     black: false,
20369     white: false,
20370      
20371     
20372
20373     /**
20374      * Protected method that will not generally be called directly. It
20375      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20376      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20377      */
20378     getDocMarkup : function(){
20379         // body styles..
20380         var st = '';
20381         
20382         // inherit styels from page...?? 
20383         if (this.stylesheets === false) {
20384             
20385             Roo.get(document.head).select('style').each(function(node) {
20386                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20387             });
20388             
20389             Roo.get(document.head).select('link').each(function(node) { 
20390                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20391             });
20392             
20393         } else if (!this.stylesheets.length) {
20394                 // simple..
20395                 st = '<style type="text/css">' +
20396                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20397                    '</style>';
20398         } else { 
20399             
20400         }
20401         
20402         st +=  '<style type="text/css">' +
20403             'IMG { cursor: pointer } ' +
20404         '</style>';
20405
20406         
20407         return '<html><head>' + st  +
20408             //<style type="text/css">' +
20409             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20410             //'</style>' +
20411             ' </head><body class="roo-htmleditor-body"></body></html>';
20412     },
20413
20414     // private
20415     onRender : function(ct, position)
20416     {
20417         var _t = this;
20418         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20419         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20420         
20421         
20422         this.el.dom.style.border = '0 none';
20423         this.el.dom.setAttribute('tabIndex', -1);
20424         this.el.addClass('x-hidden hide');
20425         
20426         
20427         
20428         if(Roo.isIE){ // fix IE 1px bogus margin
20429             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20430         }
20431        
20432         
20433         this.frameId = Roo.id();
20434         
20435          
20436         
20437         var iframe = this.owner.wrap.createChild({
20438             tag: 'iframe',
20439             cls: 'form-control', // bootstrap..
20440             id: this.frameId,
20441             name: this.frameId,
20442             frameBorder : 'no',
20443             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20444         }, this.el
20445         );
20446         
20447         
20448         this.iframe = iframe.dom;
20449
20450          this.assignDocWin();
20451         
20452         this.doc.designMode = 'on';
20453        
20454         this.doc.open();
20455         this.doc.write(this.getDocMarkup());
20456         this.doc.close();
20457
20458         
20459         var task = { // must defer to wait for browser to be ready
20460             run : function(){
20461                 //console.log("run task?" + this.doc.readyState);
20462                 this.assignDocWin();
20463                 if(this.doc.body || this.doc.readyState == 'complete'){
20464                     try {
20465                         this.doc.designMode="on";
20466                     } catch (e) {
20467                         return;
20468                     }
20469                     Roo.TaskMgr.stop(task);
20470                     this.initEditor.defer(10, this);
20471                 }
20472             },
20473             interval : 10,
20474             duration: 10000,
20475             scope: this
20476         };
20477         Roo.TaskMgr.start(task);
20478
20479     },
20480
20481     // private
20482     onResize : function(w, h)
20483     {
20484          Roo.log('resize: ' +w + ',' + h );
20485         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20486         if(!this.iframe){
20487             return;
20488         }
20489         if(typeof w == 'number'){
20490             
20491             this.iframe.style.width = w + 'px';
20492         }
20493         if(typeof h == 'number'){
20494             
20495             this.iframe.style.height = h + 'px';
20496             if(this.doc){
20497                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20498             }
20499         }
20500         
20501     },
20502
20503     /**
20504      * Toggles the editor between standard and source edit mode.
20505      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20506      */
20507     toggleSourceEdit : function(sourceEditMode){
20508         
20509         this.sourceEditMode = sourceEditMode === true;
20510         
20511         if(this.sourceEditMode){
20512  
20513             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20514             
20515         }else{
20516             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20517             //this.iframe.className = '';
20518             this.deferFocus();
20519         }
20520         //this.setSize(this.owner.wrap.getSize());
20521         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20522     },
20523
20524     
20525   
20526
20527     /**
20528      * Protected method that will not generally be called directly. If you need/want
20529      * custom HTML cleanup, this is the method you should override.
20530      * @param {String} html The HTML to be cleaned
20531      * return {String} The cleaned HTML
20532      */
20533     cleanHtml : function(html){
20534         html = String(html);
20535         if(html.length > 5){
20536             if(Roo.isSafari){ // strip safari nonsense
20537                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20538             }
20539         }
20540         if(html == '&nbsp;'){
20541             html = '';
20542         }
20543         return html;
20544     },
20545
20546     /**
20547      * HTML Editor -> Textarea
20548      * Protected method that will not generally be called directly. Syncs the contents
20549      * of the editor iframe with the textarea.
20550      */
20551     syncValue : function(){
20552         if(this.initialized){
20553             var bd = (this.doc.body || this.doc.documentElement);
20554             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20555             var html = bd.innerHTML;
20556             if(Roo.isSafari){
20557                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20558                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20559                 if(m && m[1]){
20560                     html = '<div style="'+m[0]+'">' + html + '</div>';
20561                 }
20562             }
20563             html = this.cleanHtml(html);
20564             // fix up the special chars.. normaly like back quotes in word...
20565             // however we do not want to do this with chinese..
20566             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20567                 var cc = b.charCodeAt();
20568                 if (
20569                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20570                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20571                     (cc >= 0xf900 && cc < 0xfb00 )
20572                 ) {
20573                         return b;
20574                 }
20575                 return "&#"+cc+";" 
20576             });
20577             if(this.owner.fireEvent('beforesync', this, html) !== false){
20578                 this.el.dom.value = html;
20579                 this.owner.fireEvent('sync', this, html);
20580             }
20581         }
20582     },
20583
20584     /**
20585      * Protected method that will not generally be called directly. Pushes the value of the textarea
20586      * into the iframe editor.
20587      */
20588     pushValue : function(){
20589         if(this.initialized){
20590             var v = this.el.dom.value.trim();
20591             
20592 //            if(v.length < 1){
20593 //                v = '&#160;';
20594 //            }
20595             
20596             if(this.owner.fireEvent('beforepush', this, v) !== false){
20597                 var d = (this.doc.body || this.doc.documentElement);
20598                 d.innerHTML = v;
20599                 this.cleanUpPaste();
20600                 this.el.dom.value = d.innerHTML;
20601                 this.owner.fireEvent('push', this, v);
20602             }
20603         }
20604     },
20605
20606     // private
20607     deferFocus : function(){
20608         this.focus.defer(10, this);
20609     },
20610
20611     // doc'ed in Field
20612     focus : function(){
20613         if(this.win && !this.sourceEditMode){
20614             this.win.focus();
20615         }else{
20616             this.el.focus();
20617         }
20618     },
20619     
20620     assignDocWin: function()
20621     {
20622         var iframe = this.iframe;
20623         
20624          if(Roo.isIE){
20625             this.doc = iframe.contentWindow.document;
20626             this.win = iframe.contentWindow;
20627         } else {
20628 //            if (!Roo.get(this.frameId)) {
20629 //                return;
20630 //            }
20631 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20632 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20633             
20634             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20635                 return;
20636             }
20637             
20638             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20639             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20640         }
20641     },
20642     
20643     // private
20644     initEditor : function(){
20645         //console.log("INIT EDITOR");
20646         this.assignDocWin();
20647         
20648         
20649         
20650         this.doc.designMode="on";
20651         this.doc.open();
20652         this.doc.write(this.getDocMarkup());
20653         this.doc.close();
20654         
20655         var dbody = (this.doc.body || this.doc.documentElement);
20656         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20657         // this copies styles from the containing element into thsi one..
20658         // not sure why we need all of this..
20659         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20660         
20661         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20662         //ss['background-attachment'] = 'fixed'; // w3c
20663         dbody.bgProperties = 'fixed'; // ie
20664         //Roo.DomHelper.applyStyles(dbody, ss);
20665         Roo.EventManager.on(this.doc, {
20666             //'mousedown': this.onEditorEvent,
20667             'mouseup': this.onEditorEvent,
20668             'dblclick': this.onEditorEvent,
20669             'click': this.onEditorEvent,
20670             'keyup': this.onEditorEvent,
20671             buffer:100,
20672             scope: this
20673         });
20674         if(Roo.isGecko){
20675             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20676         }
20677         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20678             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20679         }
20680         this.initialized = true;
20681
20682         this.owner.fireEvent('initialize', this);
20683         this.pushValue();
20684     },
20685
20686     // private
20687     onDestroy : function(){
20688         
20689         
20690         
20691         if(this.rendered){
20692             
20693             //for (var i =0; i < this.toolbars.length;i++) {
20694             //    // fixme - ask toolbars for heights?
20695             //    this.toolbars[i].onDestroy();
20696            // }
20697             
20698             //this.wrap.dom.innerHTML = '';
20699             //this.wrap.remove();
20700         }
20701     },
20702
20703     // private
20704     onFirstFocus : function(){
20705         
20706         this.assignDocWin();
20707         
20708         
20709         this.activated = true;
20710          
20711     
20712         if(Roo.isGecko){ // prevent silly gecko errors
20713             this.win.focus();
20714             var s = this.win.getSelection();
20715             if(!s.focusNode || s.focusNode.nodeType != 3){
20716                 var r = s.getRangeAt(0);
20717                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20718                 r.collapse(true);
20719                 this.deferFocus();
20720             }
20721             try{
20722                 this.execCmd('useCSS', true);
20723                 this.execCmd('styleWithCSS', false);
20724             }catch(e){}
20725         }
20726         this.owner.fireEvent('activate', this);
20727     },
20728
20729     // private
20730     adjustFont: function(btn){
20731         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20732         //if(Roo.isSafari){ // safari
20733         //    adjust *= 2;
20734        // }
20735         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20736         if(Roo.isSafari){ // safari
20737             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20738             v =  (v < 10) ? 10 : v;
20739             v =  (v > 48) ? 48 : v;
20740             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20741             
20742         }
20743         
20744         
20745         v = Math.max(1, v+adjust);
20746         
20747         this.execCmd('FontSize', v  );
20748     },
20749
20750     onEditorEvent : function(e)
20751     {
20752         this.owner.fireEvent('editorevent', this, e);
20753       //  this.updateToolbar();
20754         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20755     },
20756
20757     insertTag : function(tg)
20758     {
20759         // could be a bit smarter... -> wrap the current selected tRoo..
20760         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20761             
20762             range = this.createRange(this.getSelection());
20763             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20764             wrappingNode.appendChild(range.extractContents());
20765             range.insertNode(wrappingNode);
20766
20767             return;
20768             
20769             
20770             
20771         }
20772         this.execCmd("formatblock",   tg);
20773         
20774     },
20775     
20776     insertText : function(txt)
20777     {
20778         
20779         
20780         var range = this.createRange();
20781         range.deleteContents();
20782                //alert(Sender.getAttribute('label'));
20783                
20784         range.insertNode(this.doc.createTextNode(txt));
20785     } ,
20786     
20787      
20788
20789     /**
20790      * Executes a Midas editor command on the editor document and performs necessary focus and
20791      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20792      * @param {String} cmd The Midas command
20793      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20794      */
20795     relayCmd : function(cmd, value){
20796         this.win.focus();
20797         this.execCmd(cmd, value);
20798         this.owner.fireEvent('editorevent', this);
20799         //this.updateToolbar();
20800         this.owner.deferFocus();
20801     },
20802
20803     /**
20804      * Executes a Midas editor command directly on the editor document.
20805      * For visual commands, you should use {@link #relayCmd} instead.
20806      * <b>This should only be called after the editor is initialized.</b>
20807      * @param {String} cmd The Midas command
20808      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20809      */
20810     execCmd : function(cmd, value){
20811         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20812         this.syncValue();
20813     },
20814  
20815  
20816    
20817     /**
20818      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20819      * to insert tRoo.
20820      * @param {String} text | dom node.. 
20821      */
20822     insertAtCursor : function(text)
20823     {
20824         
20825         
20826         
20827         if(!this.activated){
20828             return;
20829         }
20830         /*
20831         if(Roo.isIE){
20832             this.win.focus();
20833             var r = this.doc.selection.createRange();
20834             if(r){
20835                 r.collapse(true);
20836                 r.pasteHTML(text);
20837                 this.syncValue();
20838                 this.deferFocus();
20839             
20840             }
20841             return;
20842         }
20843         */
20844         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20845             this.win.focus();
20846             
20847             
20848             // from jquery ui (MIT licenced)
20849             var range, node;
20850             var win = this.win;
20851             
20852             if (win.getSelection && win.getSelection().getRangeAt) {
20853                 range = win.getSelection().getRangeAt(0);
20854                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20855                 range.insertNode(node);
20856             } else if (win.document.selection && win.document.selection.createRange) {
20857                 // no firefox support
20858                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20859                 win.document.selection.createRange().pasteHTML(txt);
20860             } else {
20861                 // no firefox support
20862                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20863                 this.execCmd('InsertHTML', txt);
20864             } 
20865             
20866             this.syncValue();
20867             
20868             this.deferFocus();
20869         }
20870     },
20871  // private
20872     mozKeyPress : function(e){
20873         if(e.ctrlKey){
20874             var c = e.getCharCode(), cmd;
20875           
20876             if(c > 0){
20877                 c = String.fromCharCode(c).toLowerCase();
20878                 switch(c){
20879                     case 'b':
20880                         cmd = 'bold';
20881                         break;
20882                     case 'i':
20883                         cmd = 'italic';
20884                         break;
20885                     
20886                     case 'u':
20887                         cmd = 'underline';
20888                         break;
20889                     
20890                     case 'v':
20891                         this.cleanUpPaste.defer(100, this);
20892                         return;
20893                         
20894                 }
20895                 if(cmd){
20896                     this.win.focus();
20897                     this.execCmd(cmd);
20898                     this.deferFocus();
20899                     e.preventDefault();
20900                 }
20901                 
20902             }
20903         }
20904     },
20905
20906     // private
20907     fixKeys : function(){ // load time branching for fastest keydown performance
20908         if(Roo.isIE){
20909             return function(e){
20910                 var k = e.getKey(), r;
20911                 if(k == e.TAB){
20912                     e.stopEvent();
20913                     r = this.doc.selection.createRange();
20914                     if(r){
20915                         r.collapse(true);
20916                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20917                         this.deferFocus();
20918                     }
20919                     return;
20920                 }
20921                 
20922                 if(k == e.ENTER){
20923                     r = this.doc.selection.createRange();
20924                     if(r){
20925                         var target = r.parentElement();
20926                         if(!target || target.tagName.toLowerCase() != 'li'){
20927                             e.stopEvent();
20928                             r.pasteHTML('<br />');
20929                             r.collapse(false);
20930                             r.select();
20931                         }
20932                     }
20933                 }
20934                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20935                     this.cleanUpPaste.defer(100, this);
20936                     return;
20937                 }
20938                 
20939                 
20940             };
20941         }else if(Roo.isOpera){
20942             return function(e){
20943                 var k = e.getKey();
20944                 if(k == e.TAB){
20945                     e.stopEvent();
20946                     this.win.focus();
20947                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20948                     this.deferFocus();
20949                 }
20950                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20951                     this.cleanUpPaste.defer(100, this);
20952                     return;
20953                 }
20954                 
20955             };
20956         }else if(Roo.isSafari){
20957             return function(e){
20958                 var k = e.getKey();
20959                 
20960                 if(k == e.TAB){
20961                     e.stopEvent();
20962                     this.execCmd('InsertText','\t');
20963                     this.deferFocus();
20964                     return;
20965                 }
20966                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20967                     this.cleanUpPaste.defer(100, this);
20968                     return;
20969                 }
20970                 
20971              };
20972         }
20973     }(),
20974     
20975     getAllAncestors: function()
20976     {
20977         var p = this.getSelectedNode();
20978         var a = [];
20979         if (!p) {
20980             a.push(p); // push blank onto stack..
20981             p = this.getParentElement();
20982         }
20983         
20984         
20985         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20986             a.push(p);
20987             p = p.parentNode;
20988         }
20989         a.push(this.doc.body);
20990         return a;
20991     },
20992     lastSel : false,
20993     lastSelNode : false,
20994     
20995     
20996     getSelection : function() 
20997     {
20998         this.assignDocWin();
20999         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21000     },
21001     
21002     getSelectedNode: function() 
21003     {
21004         // this may only work on Gecko!!!
21005         
21006         // should we cache this!!!!
21007         
21008         
21009         
21010          
21011         var range = this.createRange(this.getSelection()).cloneRange();
21012         
21013         if (Roo.isIE) {
21014             var parent = range.parentElement();
21015             while (true) {
21016                 var testRange = range.duplicate();
21017                 testRange.moveToElementText(parent);
21018                 if (testRange.inRange(range)) {
21019                     break;
21020                 }
21021                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21022                     break;
21023                 }
21024                 parent = parent.parentElement;
21025             }
21026             return parent;
21027         }
21028         
21029         // is ancestor a text element.
21030         var ac =  range.commonAncestorContainer;
21031         if (ac.nodeType == 3) {
21032             ac = ac.parentNode;
21033         }
21034         
21035         var ar = ac.childNodes;
21036          
21037         var nodes = [];
21038         var other_nodes = [];
21039         var has_other_nodes = false;
21040         for (var i=0;i<ar.length;i++) {
21041             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21042                 continue;
21043             }
21044             // fullly contained node.
21045             
21046             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21047                 nodes.push(ar[i]);
21048                 continue;
21049             }
21050             
21051             // probably selected..
21052             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21053                 other_nodes.push(ar[i]);
21054                 continue;
21055             }
21056             // outer..
21057             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21058                 continue;
21059             }
21060             
21061             
21062             has_other_nodes = true;
21063         }
21064         if (!nodes.length && other_nodes.length) {
21065             nodes= other_nodes;
21066         }
21067         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21068             return false;
21069         }
21070         
21071         return nodes[0];
21072     },
21073     createRange: function(sel)
21074     {
21075         // this has strange effects when using with 
21076         // top toolbar - not sure if it's a great idea.
21077         //this.editor.contentWindow.focus();
21078         if (typeof sel != "undefined") {
21079             try {
21080                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21081             } catch(e) {
21082                 return this.doc.createRange();
21083             }
21084         } else {
21085             return this.doc.createRange();
21086         }
21087     },
21088     getParentElement: function()
21089     {
21090         
21091         this.assignDocWin();
21092         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21093         
21094         var range = this.createRange(sel);
21095          
21096         try {
21097             var p = range.commonAncestorContainer;
21098             while (p.nodeType == 3) { // text node
21099                 p = p.parentNode;
21100             }
21101             return p;
21102         } catch (e) {
21103             return null;
21104         }
21105     
21106     },
21107     /***
21108      *
21109      * Range intersection.. the hard stuff...
21110      *  '-1' = before
21111      *  '0' = hits..
21112      *  '1' = after.
21113      *         [ -- selected range --- ]
21114      *   [fail]                        [fail]
21115      *
21116      *    basically..
21117      *      if end is before start or  hits it. fail.
21118      *      if start is after end or hits it fail.
21119      *
21120      *   if either hits (but other is outside. - then it's not 
21121      *   
21122      *    
21123      **/
21124     
21125     
21126     // @see http://www.thismuchiknow.co.uk/?p=64.
21127     rangeIntersectsNode : function(range, node)
21128     {
21129         var nodeRange = node.ownerDocument.createRange();
21130         try {
21131             nodeRange.selectNode(node);
21132         } catch (e) {
21133             nodeRange.selectNodeContents(node);
21134         }
21135     
21136         var rangeStartRange = range.cloneRange();
21137         rangeStartRange.collapse(true);
21138     
21139         var rangeEndRange = range.cloneRange();
21140         rangeEndRange.collapse(false);
21141     
21142         var nodeStartRange = nodeRange.cloneRange();
21143         nodeStartRange.collapse(true);
21144     
21145         var nodeEndRange = nodeRange.cloneRange();
21146         nodeEndRange.collapse(false);
21147     
21148         return rangeStartRange.compareBoundaryPoints(
21149                  Range.START_TO_START, nodeEndRange) == -1 &&
21150                rangeEndRange.compareBoundaryPoints(
21151                  Range.START_TO_START, nodeStartRange) == 1;
21152         
21153          
21154     },
21155     rangeCompareNode : function(range, node)
21156     {
21157         var nodeRange = node.ownerDocument.createRange();
21158         try {
21159             nodeRange.selectNode(node);
21160         } catch (e) {
21161             nodeRange.selectNodeContents(node);
21162         }
21163         
21164         
21165         range.collapse(true);
21166     
21167         nodeRange.collapse(true);
21168      
21169         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21170         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21171          
21172         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21173         
21174         var nodeIsBefore   =  ss == 1;
21175         var nodeIsAfter    = ee == -1;
21176         
21177         if (nodeIsBefore && nodeIsAfter) {
21178             return 0; // outer
21179         }
21180         if (!nodeIsBefore && nodeIsAfter) {
21181             return 1; //right trailed.
21182         }
21183         
21184         if (nodeIsBefore && !nodeIsAfter) {
21185             return 2;  // left trailed.
21186         }
21187         // fully contined.
21188         return 3;
21189     },
21190
21191     // private? - in a new class?
21192     cleanUpPaste :  function()
21193     {
21194         // cleans up the whole document..
21195         Roo.log('cleanuppaste');
21196         
21197         this.cleanUpChildren(this.doc.body);
21198         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21199         if (clean != this.doc.body.innerHTML) {
21200             this.doc.body.innerHTML = clean;
21201         }
21202         
21203     },
21204     
21205     cleanWordChars : function(input) {// change the chars to hex code
21206         var he = Roo.HtmlEditorCore;
21207         
21208         var output = input;
21209         Roo.each(he.swapCodes, function(sw) { 
21210             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21211             
21212             output = output.replace(swapper, sw[1]);
21213         });
21214         
21215         return output;
21216     },
21217     
21218     
21219     cleanUpChildren : function (n)
21220     {
21221         if (!n.childNodes.length) {
21222             return;
21223         }
21224         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21225            this.cleanUpChild(n.childNodes[i]);
21226         }
21227     },
21228     
21229     
21230         
21231     
21232     cleanUpChild : function (node)
21233     {
21234         var ed = this;
21235         //console.log(node);
21236         if (node.nodeName == "#text") {
21237             // clean up silly Windows -- stuff?
21238             return; 
21239         }
21240         if (node.nodeName == "#comment") {
21241             node.parentNode.removeChild(node);
21242             // clean up silly Windows -- stuff?
21243             return; 
21244         }
21245         var lcname = node.tagName.toLowerCase();
21246         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21247         // whitelist of tags..
21248         
21249         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21250             // remove node.
21251             node.parentNode.removeChild(node);
21252             return;
21253             
21254         }
21255         
21256         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21257         
21258         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21259         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21260         
21261         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21262         //    remove_keep_children = true;
21263         //}
21264         
21265         if (remove_keep_children) {
21266             this.cleanUpChildren(node);
21267             // inserts everything just before this node...
21268             while (node.childNodes.length) {
21269                 var cn = node.childNodes[0];
21270                 node.removeChild(cn);
21271                 node.parentNode.insertBefore(cn, node);
21272             }
21273             node.parentNode.removeChild(node);
21274             return;
21275         }
21276         
21277         if (!node.attributes || !node.attributes.length) {
21278             this.cleanUpChildren(node);
21279             return;
21280         }
21281         
21282         function cleanAttr(n,v)
21283         {
21284             
21285             if (v.match(/^\./) || v.match(/^\//)) {
21286                 return;
21287             }
21288             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21289                 return;
21290             }
21291             if (v.match(/^#/)) {
21292                 return;
21293             }
21294 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21295             node.removeAttribute(n);
21296             
21297         }
21298         
21299         var cwhite = this.cwhite;
21300         var cblack = this.cblack;
21301             
21302         function cleanStyle(n,v)
21303         {
21304             if (v.match(/expression/)) { //XSS?? should we even bother..
21305                 node.removeAttribute(n);
21306                 return;
21307             }
21308             
21309             var parts = v.split(/;/);
21310             var clean = [];
21311             
21312             Roo.each(parts, function(p) {
21313                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21314                 if (!p.length) {
21315                     return true;
21316                 }
21317                 var l = p.split(':').shift().replace(/\s+/g,'');
21318                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21319                 
21320                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21321 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21322                     //node.removeAttribute(n);
21323                     return true;
21324                 }
21325                 //Roo.log()
21326                 // only allow 'c whitelisted system attributes'
21327                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21328 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21329                     //node.removeAttribute(n);
21330                     return true;
21331                 }
21332                 
21333                 
21334                  
21335                 
21336                 clean.push(p);
21337                 return true;
21338             });
21339             if (clean.length) { 
21340                 node.setAttribute(n, clean.join(';'));
21341             } else {
21342                 node.removeAttribute(n);
21343             }
21344             
21345         }
21346         
21347         
21348         for (var i = node.attributes.length-1; i > -1 ; i--) {
21349             var a = node.attributes[i];
21350             //console.log(a);
21351             
21352             if (a.name.toLowerCase().substr(0,2)=='on')  {
21353                 node.removeAttribute(a.name);
21354                 continue;
21355             }
21356             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21357                 node.removeAttribute(a.name);
21358                 continue;
21359             }
21360             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21361                 cleanAttr(a.name,a.value); // fixme..
21362                 continue;
21363             }
21364             if (a.name == 'style') {
21365                 cleanStyle(a.name,a.value);
21366                 continue;
21367             }
21368             /// clean up MS crap..
21369             // tecnically this should be a list of valid class'es..
21370             
21371             
21372             if (a.name == 'class') {
21373                 if (a.value.match(/^Mso/)) {
21374                     node.className = '';
21375                 }
21376                 
21377                 if (a.value.match(/body/)) {
21378                     node.className = '';
21379                 }
21380                 continue;
21381             }
21382             
21383             // style cleanup!?
21384             // class cleanup?
21385             
21386         }
21387         
21388         
21389         this.cleanUpChildren(node);
21390         
21391         
21392     },
21393     
21394     /**
21395      * Clean up MS wordisms...
21396      */
21397     cleanWord : function(node)
21398     {
21399         
21400         
21401         if (!node) {
21402             this.cleanWord(this.doc.body);
21403             return;
21404         }
21405         if (node.nodeName == "#text") {
21406             // clean up silly Windows -- stuff?
21407             return; 
21408         }
21409         if (node.nodeName == "#comment") {
21410             node.parentNode.removeChild(node);
21411             // clean up silly Windows -- stuff?
21412             return; 
21413         }
21414         
21415         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21416             node.parentNode.removeChild(node);
21417             return;
21418         }
21419         
21420         // remove - but keep children..
21421         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21422             while (node.childNodes.length) {
21423                 var cn = node.childNodes[0];
21424                 node.removeChild(cn);
21425                 node.parentNode.insertBefore(cn, node);
21426             }
21427             node.parentNode.removeChild(node);
21428             this.iterateChildren(node, this.cleanWord);
21429             return;
21430         }
21431         // clean styles
21432         if (node.className.length) {
21433             
21434             var cn = node.className.split(/\W+/);
21435             var cna = [];
21436             Roo.each(cn, function(cls) {
21437                 if (cls.match(/Mso[a-zA-Z]+/)) {
21438                     return;
21439                 }
21440                 cna.push(cls);
21441             });
21442             node.className = cna.length ? cna.join(' ') : '';
21443             if (!cna.length) {
21444                 node.removeAttribute("class");
21445             }
21446         }
21447         
21448         if (node.hasAttribute("lang")) {
21449             node.removeAttribute("lang");
21450         }
21451         
21452         if (node.hasAttribute("style")) {
21453             
21454             var styles = node.getAttribute("style").split(";");
21455             var nstyle = [];
21456             Roo.each(styles, function(s) {
21457                 if (!s.match(/:/)) {
21458                     return;
21459                 }
21460                 var kv = s.split(":");
21461                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21462                     return;
21463                 }
21464                 // what ever is left... we allow.
21465                 nstyle.push(s);
21466             });
21467             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21468             if (!nstyle.length) {
21469                 node.removeAttribute('style');
21470             }
21471         }
21472         this.iterateChildren(node, this.cleanWord);
21473         
21474         
21475         
21476     },
21477     /**
21478      * iterateChildren of a Node, calling fn each time, using this as the scole..
21479      * @param {DomNode} node node to iterate children of.
21480      * @param {Function} fn method of this class to call on each item.
21481      */
21482     iterateChildren : function(node, fn)
21483     {
21484         if (!node.childNodes.length) {
21485                 return;
21486         }
21487         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21488            fn.call(this, node.childNodes[i])
21489         }
21490     },
21491     
21492     
21493     /**
21494      * cleanTableWidths.
21495      *
21496      * Quite often pasting from word etc.. results in tables with column and widths.
21497      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21498      *
21499      */
21500     cleanTableWidths : function(node)
21501     {
21502          
21503          
21504         if (!node) {
21505             this.cleanTableWidths(this.doc.body);
21506             return;
21507         }
21508         
21509         // ignore list...
21510         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21511             return; 
21512         }
21513         Roo.log(node.tagName);
21514         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21515             this.iterateChildren(node, this.cleanTableWidths);
21516             return;
21517         }
21518         if (node.hasAttribute('width')) {
21519             node.removeAttribute('width');
21520         }
21521         
21522          
21523         if (node.hasAttribute("style")) {
21524             // pretty basic...
21525             
21526             var styles = node.getAttribute("style").split(";");
21527             var nstyle = [];
21528             Roo.each(styles, function(s) {
21529                 if (!s.match(/:/)) {
21530                     return;
21531                 }
21532                 var kv = s.split(":");
21533                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21534                     return;
21535                 }
21536                 // what ever is left... we allow.
21537                 nstyle.push(s);
21538             });
21539             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21540             if (!nstyle.length) {
21541                 node.removeAttribute('style');
21542             }
21543         }
21544         
21545         this.iterateChildren(node, this.cleanTableWidths);
21546         
21547         
21548     },
21549     
21550     
21551     
21552     
21553     domToHTML : function(currentElement, depth, nopadtext) {
21554         
21555         depth = depth || 0;
21556         nopadtext = nopadtext || false;
21557     
21558         if (!currentElement) {
21559             return this.domToHTML(this.doc.body);
21560         }
21561         
21562         //Roo.log(currentElement);
21563         var j;
21564         var allText = false;
21565         var nodeName = currentElement.nodeName;
21566         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21567         
21568         if  (nodeName == '#text') {
21569             
21570             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21571         }
21572         
21573         
21574         var ret = '';
21575         if (nodeName != 'BODY') {
21576              
21577             var i = 0;
21578             // Prints the node tagName, such as <A>, <IMG>, etc
21579             if (tagName) {
21580                 var attr = [];
21581                 for(i = 0; i < currentElement.attributes.length;i++) {
21582                     // quoting?
21583                     var aname = currentElement.attributes.item(i).name;
21584                     if (!currentElement.attributes.item(i).value.length) {
21585                         continue;
21586                     }
21587                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21588                 }
21589                 
21590                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21591             } 
21592             else {
21593                 
21594                 // eack
21595             }
21596         } else {
21597             tagName = false;
21598         }
21599         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21600             return ret;
21601         }
21602         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21603             nopadtext = true;
21604         }
21605         
21606         
21607         // Traverse the tree
21608         i = 0;
21609         var currentElementChild = currentElement.childNodes.item(i);
21610         var allText = true;
21611         var innerHTML  = '';
21612         lastnode = '';
21613         while (currentElementChild) {
21614             // Formatting code (indent the tree so it looks nice on the screen)
21615             var nopad = nopadtext;
21616             if (lastnode == 'SPAN') {
21617                 nopad  = true;
21618             }
21619             // text
21620             if  (currentElementChild.nodeName == '#text') {
21621                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21622                 toadd = nopadtext ? toadd : toadd.trim();
21623                 if (!nopad && toadd.length > 80) {
21624                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21625                 }
21626                 innerHTML  += toadd;
21627                 
21628                 i++;
21629                 currentElementChild = currentElement.childNodes.item(i);
21630                 lastNode = '';
21631                 continue;
21632             }
21633             allText = false;
21634             
21635             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21636                 
21637             // Recursively traverse the tree structure of the child node
21638             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21639             lastnode = currentElementChild.nodeName;
21640             i++;
21641             currentElementChild=currentElement.childNodes.item(i);
21642         }
21643         
21644         ret += innerHTML;
21645         
21646         if (!allText) {
21647                 // The remaining code is mostly for formatting the tree
21648             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21649         }
21650         
21651         
21652         if (tagName) {
21653             ret+= "</"+tagName+">";
21654         }
21655         return ret;
21656         
21657     },
21658         
21659     applyBlacklists : function()
21660     {
21661         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21662         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21663         
21664         this.white = [];
21665         this.black = [];
21666         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21667             if (b.indexOf(tag) > -1) {
21668                 return;
21669             }
21670             this.white.push(tag);
21671             
21672         }, this);
21673         
21674         Roo.each(w, function(tag) {
21675             if (b.indexOf(tag) > -1) {
21676                 return;
21677             }
21678             if (this.white.indexOf(tag) > -1) {
21679                 return;
21680             }
21681             this.white.push(tag);
21682             
21683         }, this);
21684         
21685         
21686         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21687             if (w.indexOf(tag) > -1) {
21688                 return;
21689             }
21690             this.black.push(tag);
21691             
21692         }, this);
21693         
21694         Roo.each(b, function(tag) {
21695             if (w.indexOf(tag) > -1) {
21696                 return;
21697             }
21698             if (this.black.indexOf(tag) > -1) {
21699                 return;
21700             }
21701             this.black.push(tag);
21702             
21703         }, this);
21704         
21705         
21706         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21707         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21708         
21709         this.cwhite = [];
21710         this.cblack = [];
21711         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21712             if (b.indexOf(tag) > -1) {
21713                 return;
21714             }
21715             this.cwhite.push(tag);
21716             
21717         }, this);
21718         
21719         Roo.each(w, function(tag) {
21720             if (b.indexOf(tag) > -1) {
21721                 return;
21722             }
21723             if (this.cwhite.indexOf(tag) > -1) {
21724                 return;
21725             }
21726             this.cwhite.push(tag);
21727             
21728         }, this);
21729         
21730         
21731         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21732             if (w.indexOf(tag) > -1) {
21733                 return;
21734             }
21735             this.cblack.push(tag);
21736             
21737         }, this);
21738         
21739         Roo.each(b, function(tag) {
21740             if (w.indexOf(tag) > -1) {
21741                 return;
21742             }
21743             if (this.cblack.indexOf(tag) > -1) {
21744                 return;
21745             }
21746             this.cblack.push(tag);
21747             
21748         }, this);
21749     },
21750     
21751     setStylesheets : function(stylesheets)
21752     {
21753         if(typeof(stylesheets) == 'string'){
21754             Roo.get(this.iframe.contentDocument.head).createChild({
21755                 tag : 'link',
21756                 rel : 'stylesheet',
21757                 type : 'text/css',
21758                 href : stylesheets
21759             });
21760             
21761             return;
21762         }
21763         var _this = this;
21764      
21765         Roo.each(stylesheets, function(s) {
21766             if(!s.length){
21767                 return;
21768             }
21769             
21770             Roo.get(_this.iframe.contentDocument.head).createChild({
21771                 tag : 'link',
21772                 rel : 'stylesheet',
21773                 type : 'text/css',
21774                 href : s
21775             });
21776         });
21777
21778         
21779     },
21780     
21781     removeStylesheets : function()
21782     {
21783         var _this = this;
21784         
21785         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21786             s.remove();
21787         });
21788     }
21789     
21790     // hide stuff that is not compatible
21791     /**
21792      * @event blur
21793      * @hide
21794      */
21795     /**
21796      * @event change
21797      * @hide
21798      */
21799     /**
21800      * @event focus
21801      * @hide
21802      */
21803     /**
21804      * @event specialkey
21805      * @hide
21806      */
21807     /**
21808      * @cfg {String} fieldClass @hide
21809      */
21810     /**
21811      * @cfg {String} focusClass @hide
21812      */
21813     /**
21814      * @cfg {String} autoCreate @hide
21815      */
21816     /**
21817      * @cfg {String} inputType @hide
21818      */
21819     /**
21820      * @cfg {String} invalidClass @hide
21821      */
21822     /**
21823      * @cfg {String} invalidText @hide
21824      */
21825     /**
21826      * @cfg {String} msgFx @hide
21827      */
21828     /**
21829      * @cfg {String} validateOnBlur @hide
21830      */
21831 });
21832
21833 Roo.HtmlEditorCore.white = [
21834         'area', 'br', 'img', 'input', 'hr', 'wbr',
21835         
21836        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21837        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21838        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21839        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21840        'table',   'ul',         'xmp', 
21841        
21842        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21843       'thead',   'tr', 
21844      
21845       'dir', 'menu', 'ol', 'ul', 'dl',
21846        
21847       'embed',  'object'
21848 ];
21849
21850
21851 Roo.HtmlEditorCore.black = [
21852     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21853         'applet', // 
21854         'base',   'basefont', 'bgsound', 'blink',  'body', 
21855         'frame',  'frameset', 'head',    'html',   'ilayer', 
21856         'iframe', 'layer',  'link',     'meta',    'object',   
21857         'script', 'style' ,'title',  'xml' // clean later..
21858 ];
21859 Roo.HtmlEditorCore.clean = [
21860     'script', 'style', 'title', 'xml'
21861 ];
21862 Roo.HtmlEditorCore.remove = [
21863     'font'
21864 ];
21865 // attributes..
21866
21867 Roo.HtmlEditorCore.ablack = [
21868     'on'
21869 ];
21870     
21871 Roo.HtmlEditorCore.aclean = [ 
21872     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21873 ];
21874
21875 // protocols..
21876 Roo.HtmlEditorCore.pwhite= [
21877         'http',  'https',  'mailto'
21878 ];
21879
21880 // white listed style attributes.
21881 Roo.HtmlEditorCore.cwhite= [
21882       //  'text-align', /// default is to allow most things..
21883       
21884          
21885 //        'font-size'//??
21886 ];
21887
21888 // black listed style attributes.
21889 Roo.HtmlEditorCore.cblack= [
21890       //  'font-size' -- this can be set by the project 
21891 ];
21892
21893
21894 Roo.HtmlEditorCore.swapCodes   =[ 
21895     [    8211, "--" ], 
21896     [    8212, "--" ], 
21897     [    8216,  "'" ],  
21898     [    8217, "'" ],  
21899     [    8220, '"' ],  
21900     [    8221, '"' ],  
21901     [    8226, "*" ],  
21902     [    8230, "..." ]
21903 ]; 
21904
21905     /*
21906  * - LGPL
21907  *
21908  * HtmlEditor
21909  * 
21910  */
21911
21912 /**
21913  * @class Roo.bootstrap.HtmlEditor
21914  * @extends Roo.bootstrap.TextArea
21915  * Bootstrap HtmlEditor class
21916
21917  * @constructor
21918  * Create a new HtmlEditor
21919  * @param {Object} config The config object
21920  */
21921
21922 Roo.bootstrap.HtmlEditor = function(config){
21923     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21924     if (!this.toolbars) {
21925         this.toolbars = [];
21926     }
21927     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21928     this.addEvents({
21929             /**
21930              * @event initialize
21931              * Fires when the editor is fully initialized (including the iframe)
21932              * @param {HtmlEditor} this
21933              */
21934             initialize: true,
21935             /**
21936              * @event activate
21937              * Fires when the editor is first receives the focus. Any insertion must wait
21938              * until after this event.
21939              * @param {HtmlEditor} this
21940              */
21941             activate: true,
21942              /**
21943              * @event beforesync
21944              * Fires before the textarea is updated with content from the editor iframe. Return false
21945              * to cancel the sync.
21946              * @param {HtmlEditor} this
21947              * @param {String} html
21948              */
21949             beforesync: true,
21950              /**
21951              * @event beforepush
21952              * Fires before the iframe editor is updated with content from the textarea. Return false
21953              * to cancel the push.
21954              * @param {HtmlEditor} this
21955              * @param {String} html
21956              */
21957             beforepush: true,
21958              /**
21959              * @event sync
21960              * Fires when the textarea is updated with content from the editor iframe.
21961              * @param {HtmlEditor} this
21962              * @param {String} html
21963              */
21964             sync: true,
21965              /**
21966              * @event push
21967              * Fires when the iframe editor is updated with content from the textarea.
21968              * @param {HtmlEditor} this
21969              * @param {String} html
21970              */
21971             push: true,
21972              /**
21973              * @event editmodechange
21974              * Fires when the editor switches edit modes
21975              * @param {HtmlEditor} this
21976              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21977              */
21978             editmodechange: true,
21979             /**
21980              * @event editorevent
21981              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21982              * @param {HtmlEditor} this
21983              */
21984             editorevent: true,
21985             /**
21986              * @event firstfocus
21987              * Fires when on first focus - needed by toolbars..
21988              * @param {HtmlEditor} this
21989              */
21990             firstfocus: true,
21991             /**
21992              * @event autosave
21993              * Auto save the htmlEditor value as a file into Events
21994              * @param {HtmlEditor} this
21995              */
21996             autosave: true,
21997             /**
21998              * @event savedpreview
21999              * preview the saved version of htmlEditor
22000              * @param {HtmlEditor} this
22001              */
22002             savedpreview: true
22003         });
22004 };
22005
22006
22007 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22008     
22009     
22010       /**
22011      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22012      */
22013     toolbars : false,
22014    
22015      /**
22016      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22017      *                        Roo.resizable.
22018      */
22019     resizable : false,
22020      /**
22021      * @cfg {Number} height (in pixels)
22022      */   
22023     height: 300,
22024    /**
22025      * @cfg {Number} width (in pixels)
22026      */   
22027     width: false,
22028     
22029     /**
22030      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22031      * 
22032      */
22033     stylesheets: false,
22034     
22035     // id of frame..
22036     frameId: false,
22037     
22038     // private properties
22039     validationEvent : false,
22040     deferHeight: true,
22041     initialized : false,
22042     activated : false,
22043     
22044     onFocus : Roo.emptyFn,
22045     iframePad:3,
22046     hideMode:'offsets',
22047     
22048     
22049     tbContainer : false,
22050     
22051     toolbarContainer :function() {
22052         return this.wrap.select('.x-html-editor-tb',true).first();
22053     },
22054
22055     /**
22056      * Protected method that will not generally be called directly. It
22057      * is called when the editor creates its toolbar. Override this method if you need to
22058      * add custom toolbar buttons.
22059      * @param {HtmlEditor} editor
22060      */
22061     createToolbar : function(){
22062         
22063         Roo.log("create toolbars");
22064         
22065         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22066         this.toolbars[0].render(this.toolbarContainer());
22067         
22068         return;
22069         
22070 //        if (!editor.toolbars || !editor.toolbars.length) {
22071 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22072 //        }
22073 //        
22074 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22075 //            editor.toolbars[i] = Roo.factory(
22076 //                    typeof(editor.toolbars[i]) == 'string' ?
22077 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22078 //                Roo.bootstrap.HtmlEditor);
22079 //            editor.toolbars[i].init(editor);
22080 //        }
22081     },
22082
22083      
22084     // private
22085     onRender : function(ct, position)
22086     {
22087        // Roo.log("Call onRender: " + this.xtype);
22088         var _t = this;
22089         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22090       
22091         this.wrap = this.inputEl().wrap({
22092             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22093         });
22094         
22095         this.editorcore.onRender(ct, position);
22096          
22097         if (this.resizable) {
22098             this.resizeEl = new Roo.Resizable(this.wrap, {
22099                 pinned : true,
22100                 wrap: true,
22101                 dynamic : true,
22102                 minHeight : this.height,
22103                 height: this.height,
22104                 handles : this.resizable,
22105                 width: this.width,
22106                 listeners : {
22107                     resize : function(r, w, h) {
22108                         _t.onResize(w,h); // -something
22109                     }
22110                 }
22111             });
22112             
22113         }
22114         this.createToolbar(this);
22115        
22116         
22117         if(!this.width && this.resizable){
22118             this.setSize(this.wrap.getSize());
22119         }
22120         if (this.resizeEl) {
22121             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22122             // should trigger onReize..
22123         }
22124         
22125     },
22126
22127     // private
22128     onResize : function(w, h)
22129     {
22130         Roo.log('resize: ' +w + ',' + h );
22131         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22132         var ew = false;
22133         var eh = false;
22134         
22135         if(this.inputEl() ){
22136             if(typeof w == 'number'){
22137                 var aw = w - this.wrap.getFrameWidth('lr');
22138                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22139                 ew = aw;
22140             }
22141             if(typeof h == 'number'){
22142                  var tbh = -11;  // fixme it needs to tool bar size!
22143                 for (var i =0; i < this.toolbars.length;i++) {
22144                     // fixme - ask toolbars for heights?
22145                     tbh += this.toolbars[i].el.getHeight();
22146                     //if (this.toolbars[i].footer) {
22147                     //    tbh += this.toolbars[i].footer.el.getHeight();
22148                     //}
22149                 }
22150               
22151                 
22152                 
22153                 
22154                 
22155                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22156                 ah -= 5; // knock a few pixes off for look..
22157                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22158                 var eh = ah;
22159             }
22160         }
22161         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22162         this.editorcore.onResize(ew,eh);
22163         
22164     },
22165
22166     /**
22167      * Toggles the editor between standard and source edit mode.
22168      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22169      */
22170     toggleSourceEdit : function(sourceEditMode)
22171     {
22172         this.editorcore.toggleSourceEdit(sourceEditMode);
22173         
22174         if(this.editorcore.sourceEditMode){
22175             Roo.log('editor - showing textarea');
22176             
22177 //            Roo.log('in');
22178 //            Roo.log(this.syncValue());
22179             this.syncValue();
22180             this.inputEl().removeClass(['hide', 'x-hidden']);
22181             this.inputEl().dom.removeAttribute('tabIndex');
22182             this.inputEl().focus();
22183         }else{
22184             Roo.log('editor - hiding textarea');
22185 //            Roo.log('out')
22186 //            Roo.log(this.pushValue()); 
22187             this.pushValue();
22188             
22189             this.inputEl().addClass(['hide', 'x-hidden']);
22190             this.inputEl().dom.setAttribute('tabIndex', -1);
22191             //this.deferFocus();
22192         }
22193          
22194         if(this.resizable){
22195             this.setSize(this.wrap.getSize());
22196         }
22197         
22198         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22199     },
22200  
22201     // private (for BoxComponent)
22202     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22203
22204     // private (for BoxComponent)
22205     getResizeEl : function(){
22206         return this.wrap;
22207     },
22208
22209     // private (for BoxComponent)
22210     getPositionEl : function(){
22211         return this.wrap;
22212     },
22213
22214     // private
22215     initEvents : function(){
22216         this.originalValue = this.getValue();
22217     },
22218
22219 //    /**
22220 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22221 //     * @method
22222 //     */
22223 //    markInvalid : Roo.emptyFn,
22224 //    /**
22225 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22226 //     * @method
22227 //     */
22228 //    clearInvalid : Roo.emptyFn,
22229
22230     setValue : function(v){
22231         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22232         this.editorcore.pushValue();
22233     },
22234
22235      
22236     // private
22237     deferFocus : function(){
22238         this.focus.defer(10, this);
22239     },
22240
22241     // doc'ed in Field
22242     focus : function(){
22243         this.editorcore.focus();
22244         
22245     },
22246       
22247
22248     // private
22249     onDestroy : function(){
22250         
22251         
22252         
22253         if(this.rendered){
22254             
22255             for (var i =0; i < this.toolbars.length;i++) {
22256                 // fixme - ask toolbars for heights?
22257                 this.toolbars[i].onDestroy();
22258             }
22259             
22260             this.wrap.dom.innerHTML = '';
22261             this.wrap.remove();
22262         }
22263     },
22264
22265     // private
22266     onFirstFocus : function(){
22267         //Roo.log("onFirstFocus");
22268         this.editorcore.onFirstFocus();
22269          for (var i =0; i < this.toolbars.length;i++) {
22270             this.toolbars[i].onFirstFocus();
22271         }
22272         
22273     },
22274     
22275     // private
22276     syncValue : function()
22277     {   
22278         this.editorcore.syncValue();
22279     },
22280     
22281     pushValue : function()
22282     {   
22283         this.editorcore.pushValue();
22284     }
22285      
22286     
22287     // hide stuff that is not compatible
22288     /**
22289      * @event blur
22290      * @hide
22291      */
22292     /**
22293      * @event change
22294      * @hide
22295      */
22296     /**
22297      * @event focus
22298      * @hide
22299      */
22300     /**
22301      * @event specialkey
22302      * @hide
22303      */
22304     /**
22305      * @cfg {String} fieldClass @hide
22306      */
22307     /**
22308      * @cfg {String} focusClass @hide
22309      */
22310     /**
22311      * @cfg {String} autoCreate @hide
22312      */
22313     /**
22314      * @cfg {String} inputType @hide
22315      */
22316     /**
22317      * @cfg {String} invalidClass @hide
22318      */
22319     /**
22320      * @cfg {String} invalidText @hide
22321      */
22322     /**
22323      * @cfg {String} msgFx @hide
22324      */
22325     /**
22326      * @cfg {String} validateOnBlur @hide
22327      */
22328 });
22329  
22330     
22331    
22332    
22333    
22334       
22335 Roo.namespace('Roo.bootstrap.htmleditor');
22336 /**
22337  * @class Roo.bootstrap.HtmlEditorToolbar1
22338  * Basic Toolbar
22339  * 
22340  * Usage:
22341  *
22342  new Roo.bootstrap.HtmlEditor({
22343     ....
22344     toolbars : [
22345         new Roo.bootstrap.HtmlEditorToolbar1({
22346             disable : { fonts: 1 , format: 1, ..., ... , ...],
22347             btns : [ .... ]
22348         })
22349     }
22350      
22351  * 
22352  * @cfg {Object} disable List of elements to disable..
22353  * @cfg {Array} btns List of additional buttons.
22354  * 
22355  * 
22356  * NEEDS Extra CSS? 
22357  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22358  */
22359  
22360 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22361 {
22362     
22363     Roo.apply(this, config);
22364     
22365     // default disabled, based on 'good practice'..
22366     this.disable = this.disable || {};
22367     Roo.applyIf(this.disable, {
22368         fontSize : true,
22369         colors : true,
22370         specialElements : true
22371     });
22372     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22373     
22374     this.editor = config.editor;
22375     this.editorcore = config.editor.editorcore;
22376     
22377     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22378     
22379     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22380     // dont call parent... till later.
22381 }
22382 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22383      
22384     bar : true,
22385     
22386     editor : false,
22387     editorcore : false,
22388     
22389     
22390     formats : [
22391         "p" ,  
22392         "h1","h2","h3","h4","h5","h6", 
22393         "pre", "code", 
22394         "abbr", "acronym", "address", "cite", "samp", "var",
22395         'div','span'
22396     ],
22397     
22398     onRender : function(ct, position)
22399     {
22400        // Roo.log("Call onRender: " + this.xtype);
22401         
22402        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22403        Roo.log(this.el);
22404        this.el.dom.style.marginBottom = '0';
22405        var _this = this;
22406        var editorcore = this.editorcore;
22407        var editor= this.editor;
22408        
22409        var children = [];
22410        var btn = function(id,cmd , toggle, handler){
22411        
22412             var  event = toggle ? 'toggle' : 'click';
22413        
22414             var a = {
22415                 size : 'sm',
22416                 xtype: 'Button',
22417                 xns: Roo.bootstrap,
22418                 glyphicon : id,
22419                 cmd : id || cmd,
22420                 enableToggle:toggle !== false,
22421                 //html : 'submit'
22422                 pressed : toggle ? false : null,
22423                 listeners : {}
22424             };
22425             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22426                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22427             };
22428             children.push(a);
22429             return a;
22430        }
22431         
22432         var style = {
22433                 xtype: 'Button',
22434                 size : 'sm',
22435                 xns: Roo.bootstrap,
22436                 glyphicon : 'font',
22437                 //html : 'submit'
22438                 menu : {
22439                     xtype: 'Menu',
22440                     xns: Roo.bootstrap,
22441                     items:  []
22442                 }
22443         };
22444         Roo.each(this.formats, function(f) {
22445             style.menu.items.push({
22446                 xtype :'MenuItem',
22447                 xns: Roo.bootstrap,
22448                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22449                 tagname : f,
22450                 listeners : {
22451                     click : function()
22452                     {
22453                         editorcore.insertTag(this.tagname);
22454                         editor.focus();
22455                     }
22456                 }
22457                 
22458             });
22459         });
22460          children.push(style);   
22461             
22462             
22463         btn('bold',false,true);
22464         btn('italic',false,true);
22465         btn('align-left', 'justifyleft',true);
22466         btn('align-center', 'justifycenter',true);
22467         btn('align-right' , 'justifyright',true);
22468         btn('link', false, false, function(btn) {
22469             //Roo.log("create link?");
22470             var url = prompt(this.createLinkText, this.defaultLinkValue);
22471             if(url && url != 'http:/'+'/'){
22472                 this.editorcore.relayCmd('createlink', url);
22473             }
22474         }),
22475         btn('list','insertunorderedlist',true);
22476         btn('pencil', false,true, function(btn){
22477                 Roo.log(this);
22478                 
22479                 this.toggleSourceEdit(btn.pressed);
22480         });
22481         /*
22482         var cog = {
22483                 xtype: 'Button',
22484                 size : 'sm',
22485                 xns: Roo.bootstrap,
22486                 glyphicon : 'cog',
22487                 //html : 'submit'
22488                 menu : {
22489                     xtype: 'Menu',
22490                     xns: Roo.bootstrap,
22491                     items:  []
22492                 }
22493         };
22494         
22495         cog.menu.items.push({
22496             xtype :'MenuItem',
22497             xns: Roo.bootstrap,
22498             html : Clean styles,
22499             tagname : f,
22500             listeners : {
22501                 click : function()
22502                 {
22503                     editorcore.insertTag(this.tagname);
22504                     editor.focus();
22505                 }
22506             }
22507             
22508         });
22509        */
22510         
22511          
22512        this.xtype = 'NavSimplebar';
22513         
22514         for(var i=0;i< children.length;i++) {
22515             
22516             this.buttons.add(this.addxtypeChild(children[i]));
22517             
22518         }
22519         
22520         editor.on('editorevent', this.updateToolbar, this);
22521     },
22522     onBtnClick : function(id)
22523     {
22524        this.editorcore.relayCmd(id);
22525        this.editorcore.focus();
22526     },
22527     
22528     /**
22529      * Protected method that will not generally be called directly. It triggers
22530      * a toolbar update by reading the markup state of the current selection in the editor.
22531      */
22532     updateToolbar: function(){
22533
22534         if(!this.editorcore.activated){
22535             this.editor.onFirstFocus(); // is this neeed?
22536             return;
22537         }
22538
22539         var btns = this.buttons; 
22540         var doc = this.editorcore.doc;
22541         btns.get('bold').setActive(doc.queryCommandState('bold'));
22542         btns.get('italic').setActive(doc.queryCommandState('italic'));
22543         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22544         
22545         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22546         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22547         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22548         
22549         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22550         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22551          /*
22552         
22553         var ans = this.editorcore.getAllAncestors();
22554         if (this.formatCombo) {
22555             
22556             
22557             var store = this.formatCombo.store;
22558             this.formatCombo.setValue("");
22559             for (var i =0; i < ans.length;i++) {
22560                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22561                     // select it..
22562                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22563                     break;
22564                 }
22565             }
22566         }
22567         
22568         
22569         
22570         // hides menus... - so this cant be on a menu...
22571         Roo.bootstrap.MenuMgr.hideAll();
22572         */
22573         Roo.bootstrap.MenuMgr.hideAll();
22574         //this.editorsyncValue();
22575     },
22576     onFirstFocus: function() {
22577         this.buttons.each(function(item){
22578            item.enable();
22579         });
22580     },
22581     toggleSourceEdit : function(sourceEditMode){
22582         
22583           
22584         if(sourceEditMode){
22585             Roo.log("disabling buttons");
22586            this.buttons.each( function(item){
22587                 if(item.cmd != 'pencil'){
22588                     item.disable();
22589                 }
22590             });
22591           
22592         }else{
22593             Roo.log("enabling buttons");
22594             if(this.editorcore.initialized){
22595                 this.buttons.each( function(item){
22596                     item.enable();
22597                 });
22598             }
22599             
22600         }
22601         Roo.log("calling toggole on editor");
22602         // tell the editor that it's been pressed..
22603         this.editor.toggleSourceEdit(sourceEditMode);
22604        
22605     }
22606 });
22607
22608
22609
22610
22611
22612 /**
22613  * @class Roo.bootstrap.Table.AbstractSelectionModel
22614  * @extends Roo.util.Observable
22615  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22616  * implemented by descendant classes.  This class should not be directly instantiated.
22617  * @constructor
22618  */
22619 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22620     this.locked = false;
22621     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22622 };
22623
22624
22625 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22626     /** @ignore Called by the grid automatically. Do not call directly. */
22627     init : function(grid){
22628         this.grid = grid;
22629         this.initEvents();
22630     },
22631
22632     /**
22633      * Locks the selections.
22634      */
22635     lock : function(){
22636         this.locked = true;
22637     },
22638
22639     /**
22640      * Unlocks the selections.
22641      */
22642     unlock : function(){
22643         this.locked = false;
22644     },
22645
22646     /**
22647      * Returns true if the selections are locked.
22648      * @return {Boolean}
22649      */
22650     isLocked : function(){
22651         return this.locked;
22652     }
22653 });
22654 /**
22655  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22656  * @class Roo.bootstrap.Table.RowSelectionModel
22657  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22658  * It supports multiple selections and keyboard selection/navigation. 
22659  * @constructor
22660  * @param {Object} config
22661  */
22662
22663 Roo.bootstrap.Table.RowSelectionModel = function(config){
22664     Roo.apply(this, config);
22665     this.selections = new Roo.util.MixedCollection(false, function(o){
22666         return o.id;
22667     });
22668
22669     this.last = false;
22670     this.lastActive = false;
22671
22672     this.addEvents({
22673         /**
22674              * @event selectionchange
22675              * Fires when the selection changes
22676              * @param {SelectionModel} this
22677              */
22678             "selectionchange" : true,
22679         /**
22680              * @event afterselectionchange
22681              * Fires after the selection changes (eg. by key press or clicking)
22682              * @param {SelectionModel} this
22683              */
22684             "afterselectionchange" : true,
22685         /**
22686              * @event beforerowselect
22687              * Fires when a row is selected being selected, return false to cancel.
22688              * @param {SelectionModel} this
22689              * @param {Number} rowIndex The selected index
22690              * @param {Boolean} keepExisting False if other selections will be cleared
22691              */
22692             "beforerowselect" : true,
22693         /**
22694              * @event rowselect
22695              * Fires when a row is selected.
22696              * @param {SelectionModel} this
22697              * @param {Number} rowIndex The selected index
22698              * @param {Roo.data.Record} r The record
22699              */
22700             "rowselect" : true,
22701         /**
22702              * @event rowdeselect
22703              * Fires when a row is deselected.
22704              * @param {SelectionModel} this
22705              * @param {Number} rowIndex The selected index
22706              */
22707         "rowdeselect" : true
22708     });
22709     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22710     this.locked = false;
22711  };
22712
22713 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22714     /**
22715      * @cfg {Boolean} singleSelect
22716      * True to allow selection of only one row at a time (defaults to false)
22717      */
22718     singleSelect : false,
22719
22720     // private
22721     initEvents : function()
22722     {
22723
22724         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22725         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22726         //}else{ // allow click to work like normal
22727          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22728         //}
22729         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22730         this.grid.on("rowclick", this.handleMouseDown, this);
22731         
22732         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22733             "up" : function(e){
22734                 if(!e.shiftKey){
22735                     this.selectPrevious(e.shiftKey);
22736                 }else if(this.last !== false && this.lastActive !== false){
22737                     var last = this.last;
22738                     this.selectRange(this.last,  this.lastActive-1);
22739                     this.grid.getView().focusRow(this.lastActive);
22740                     if(last !== false){
22741                         this.last = last;
22742                     }
22743                 }else{
22744                     this.selectFirstRow();
22745                 }
22746                 this.fireEvent("afterselectionchange", this);
22747             },
22748             "down" : function(e){
22749                 if(!e.shiftKey){
22750                     this.selectNext(e.shiftKey);
22751                 }else if(this.last !== false && this.lastActive !== false){
22752                     var last = this.last;
22753                     this.selectRange(this.last,  this.lastActive+1);
22754                     this.grid.getView().focusRow(this.lastActive);
22755                     if(last !== false){
22756                         this.last = last;
22757                     }
22758                 }else{
22759                     this.selectFirstRow();
22760                 }
22761                 this.fireEvent("afterselectionchange", this);
22762             },
22763             scope: this
22764         });
22765         this.grid.store.on('load', function(){
22766             this.selections.clear();
22767         },this);
22768         /*
22769         var view = this.grid.view;
22770         view.on("refresh", this.onRefresh, this);
22771         view.on("rowupdated", this.onRowUpdated, this);
22772         view.on("rowremoved", this.onRemove, this);
22773         */
22774     },
22775
22776     // private
22777     onRefresh : function()
22778     {
22779         var ds = this.grid.store, i, v = this.grid.view;
22780         var s = this.selections;
22781         s.each(function(r){
22782             if((i = ds.indexOfId(r.id)) != -1){
22783                 v.onRowSelect(i);
22784             }else{
22785                 s.remove(r);
22786             }
22787         });
22788     },
22789
22790     // private
22791     onRemove : function(v, index, r){
22792         this.selections.remove(r);
22793     },
22794
22795     // private
22796     onRowUpdated : function(v, index, r){
22797         if(this.isSelected(r)){
22798             v.onRowSelect(index);
22799         }
22800     },
22801
22802     /**
22803      * Select records.
22804      * @param {Array} records The records to select
22805      * @param {Boolean} keepExisting (optional) True to keep existing selections
22806      */
22807     selectRecords : function(records, keepExisting)
22808     {
22809         if(!keepExisting){
22810             this.clearSelections();
22811         }
22812             var ds = this.grid.store;
22813         for(var i = 0, len = records.length; i < len; i++){
22814             this.selectRow(ds.indexOf(records[i]), true);
22815         }
22816     },
22817
22818     /**
22819      * Gets the number of selected rows.
22820      * @return {Number}
22821      */
22822     getCount : function(){
22823         return this.selections.length;
22824     },
22825
22826     /**
22827      * Selects the first row in the grid.
22828      */
22829     selectFirstRow : function(){
22830         this.selectRow(0);
22831     },
22832
22833     /**
22834      * Select the last row.
22835      * @param {Boolean} keepExisting (optional) True to keep existing selections
22836      */
22837     selectLastRow : function(keepExisting){
22838         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22839         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22840     },
22841
22842     /**
22843      * Selects the row immediately following the last selected row.
22844      * @param {Boolean} keepExisting (optional) True to keep existing selections
22845      */
22846     selectNext : function(keepExisting)
22847     {
22848             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22849             this.selectRow(this.last+1, keepExisting);
22850             this.grid.getView().focusRow(this.last);
22851         }
22852     },
22853
22854     /**
22855      * Selects the row that precedes the last selected row.
22856      * @param {Boolean} keepExisting (optional) True to keep existing selections
22857      */
22858     selectPrevious : function(keepExisting){
22859         if(this.last){
22860             this.selectRow(this.last-1, keepExisting);
22861             this.grid.getView().focusRow(this.last);
22862         }
22863     },
22864
22865     /**
22866      * Returns the selected records
22867      * @return {Array} Array of selected records
22868      */
22869     getSelections : function(){
22870         return [].concat(this.selections.items);
22871     },
22872
22873     /**
22874      * Returns the first selected record.
22875      * @return {Record}
22876      */
22877     getSelected : function(){
22878         return this.selections.itemAt(0);
22879     },
22880
22881
22882     /**
22883      * Clears all selections.
22884      */
22885     clearSelections : function(fast)
22886     {
22887         if(this.locked) {
22888             return;
22889         }
22890         if(fast !== true){
22891                 var ds = this.grid.store;
22892             var s = this.selections;
22893             s.each(function(r){
22894                 this.deselectRow(ds.indexOfId(r.id));
22895             }, this);
22896             s.clear();
22897         }else{
22898             this.selections.clear();
22899         }
22900         this.last = false;
22901     },
22902
22903
22904     /**
22905      * Selects all rows.
22906      */
22907     selectAll : function(){
22908         if(this.locked) {
22909             return;
22910         }
22911         this.selections.clear();
22912         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22913             this.selectRow(i, true);
22914         }
22915     },
22916
22917     /**
22918      * Returns True if there is a selection.
22919      * @return {Boolean}
22920      */
22921     hasSelection : function(){
22922         return this.selections.length > 0;
22923     },
22924
22925     /**
22926      * Returns True if the specified row is selected.
22927      * @param {Number/Record} record The record or index of the record to check
22928      * @return {Boolean}
22929      */
22930     isSelected : function(index){
22931             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22932         return (r && this.selections.key(r.id) ? true : false);
22933     },
22934
22935     /**
22936      * Returns True if the specified record id is selected.
22937      * @param {String} id The id of record to check
22938      * @return {Boolean}
22939      */
22940     isIdSelected : function(id){
22941         return (this.selections.key(id) ? true : false);
22942     },
22943
22944
22945     // private
22946     handleMouseDBClick : function(e, t){
22947         
22948     },
22949     // private
22950     handleMouseDown : function(e, t)
22951     {
22952             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22953         if(this.isLocked() || rowIndex < 0 ){
22954             return;
22955         };
22956         if(e.shiftKey && this.last !== false){
22957             var last = this.last;
22958             this.selectRange(last, rowIndex, e.ctrlKey);
22959             this.last = last; // reset the last
22960             t.focus();
22961     
22962         }else{
22963             var isSelected = this.isSelected(rowIndex);
22964             //Roo.log("select row:" + rowIndex);
22965             if(isSelected){
22966                 this.deselectRow(rowIndex);
22967             } else {
22968                         this.selectRow(rowIndex, true);
22969             }
22970     
22971             /*
22972                 if(e.button !== 0 && isSelected){
22973                 alert('rowIndex 2: ' + rowIndex);
22974                     view.focusRow(rowIndex);
22975                 }else if(e.ctrlKey && isSelected){
22976                     this.deselectRow(rowIndex);
22977                 }else if(!isSelected){
22978                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22979                     view.focusRow(rowIndex);
22980                 }
22981             */
22982         }
22983         this.fireEvent("afterselectionchange", this);
22984     },
22985     // private
22986     handleDragableRowClick :  function(grid, rowIndex, e) 
22987     {
22988         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22989             this.selectRow(rowIndex, false);
22990             grid.view.focusRow(rowIndex);
22991              this.fireEvent("afterselectionchange", this);
22992         }
22993     },
22994     
22995     /**
22996      * Selects multiple rows.
22997      * @param {Array} rows Array of the indexes of the row to select
22998      * @param {Boolean} keepExisting (optional) True to keep existing selections
22999      */
23000     selectRows : function(rows, keepExisting){
23001         if(!keepExisting){
23002             this.clearSelections();
23003         }
23004         for(var i = 0, len = rows.length; i < len; i++){
23005             this.selectRow(rows[i], true);
23006         }
23007     },
23008
23009     /**
23010      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23011      * @param {Number} startRow The index of the first row in the range
23012      * @param {Number} endRow The index of the last row in the range
23013      * @param {Boolean} keepExisting (optional) True to retain existing selections
23014      */
23015     selectRange : function(startRow, endRow, keepExisting){
23016         if(this.locked) {
23017             return;
23018         }
23019         if(!keepExisting){
23020             this.clearSelections();
23021         }
23022         if(startRow <= endRow){
23023             for(var i = startRow; i <= endRow; i++){
23024                 this.selectRow(i, true);
23025             }
23026         }else{
23027             for(var i = startRow; i >= endRow; i--){
23028                 this.selectRow(i, true);
23029             }
23030         }
23031     },
23032
23033     /**
23034      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23035      * @param {Number} startRow The index of the first row in the range
23036      * @param {Number} endRow The index of the last row in the range
23037      */
23038     deselectRange : function(startRow, endRow, preventViewNotify){
23039         if(this.locked) {
23040             return;
23041         }
23042         for(var i = startRow; i <= endRow; i++){
23043             this.deselectRow(i, preventViewNotify);
23044         }
23045     },
23046
23047     /**
23048      * Selects a row.
23049      * @param {Number} row The index of the row to select
23050      * @param {Boolean} keepExisting (optional) True to keep existing selections
23051      */
23052     selectRow : function(index, keepExisting, preventViewNotify)
23053     {
23054             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23055             return;
23056         }
23057         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23058             if(!keepExisting || this.singleSelect){
23059                 this.clearSelections();
23060             }
23061             
23062             var r = this.grid.store.getAt(index);
23063             //console.log('selectRow - record id :' + r.id);
23064             
23065             this.selections.add(r);
23066             this.last = this.lastActive = index;
23067             if(!preventViewNotify){
23068                 var proxy = new Roo.Element(
23069                                 this.grid.getRowDom(index)
23070                 );
23071                 proxy.addClass('bg-info info');
23072             }
23073             this.fireEvent("rowselect", this, index, r);
23074             this.fireEvent("selectionchange", this);
23075         }
23076     },
23077
23078     /**
23079      * Deselects a row.
23080      * @param {Number} row The index of the row to deselect
23081      */
23082     deselectRow : function(index, preventViewNotify)
23083     {
23084         if(this.locked) {
23085             return;
23086         }
23087         if(this.last == index){
23088             this.last = false;
23089         }
23090         if(this.lastActive == index){
23091             this.lastActive = false;
23092         }
23093         
23094         var r = this.grid.store.getAt(index);
23095         if (!r) {
23096             return;
23097         }
23098         
23099         this.selections.remove(r);
23100         //.console.log('deselectRow - record id :' + r.id);
23101         if(!preventViewNotify){
23102         
23103             var proxy = new Roo.Element(
23104                 this.grid.getRowDom(index)
23105             );
23106             proxy.removeClass('bg-info info');
23107         }
23108         this.fireEvent("rowdeselect", this, index);
23109         this.fireEvent("selectionchange", this);
23110     },
23111
23112     // private
23113     restoreLast : function(){
23114         if(this._last){
23115             this.last = this._last;
23116         }
23117     },
23118
23119     // private
23120     acceptsNav : function(row, col, cm){
23121         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23122     },
23123
23124     // private
23125     onEditorKey : function(field, e){
23126         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23127         if(k == e.TAB){
23128             e.stopEvent();
23129             ed.completeEdit();
23130             if(e.shiftKey){
23131                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23132             }else{
23133                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23134             }
23135         }else if(k == e.ENTER && !e.ctrlKey){
23136             e.stopEvent();
23137             ed.completeEdit();
23138             if(e.shiftKey){
23139                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23140             }else{
23141                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23142             }
23143         }else if(k == e.ESC){
23144             ed.cancelEdit();
23145         }
23146         if(newCell){
23147             g.startEditing(newCell[0], newCell[1]);
23148         }
23149     }
23150 });
23151 /*
23152  * Based on:
23153  * Ext JS Library 1.1.1
23154  * Copyright(c) 2006-2007, Ext JS, LLC.
23155  *
23156  * Originally Released Under LGPL - original licence link has changed is not relivant.
23157  *
23158  * Fork - LGPL
23159  * <script type="text/javascript">
23160  */
23161  
23162 /**
23163  * @class Roo.bootstrap.PagingToolbar
23164  * @extends Roo.bootstrap.NavSimplebar
23165  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23166  * @constructor
23167  * Create a new PagingToolbar
23168  * @param {Object} config The config object
23169  * @param {Roo.data.Store} store
23170  */
23171 Roo.bootstrap.PagingToolbar = function(config)
23172 {
23173     // old args format still supported... - xtype is prefered..
23174         // created from xtype...
23175     
23176     this.ds = config.dataSource;
23177     
23178     if (config.store && !this.ds) {
23179         this.store= Roo.factory(config.store, Roo.data);
23180         this.ds = this.store;
23181         this.ds.xmodule = this.xmodule || false;
23182     }
23183     
23184     this.toolbarItems = [];
23185     if (config.items) {
23186         this.toolbarItems = config.items;
23187     }
23188     
23189     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23190     
23191     this.cursor = 0;
23192     
23193     if (this.ds) { 
23194         this.bind(this.ds);
23195     }
23196     
23197     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23198     
23199 };
23200
23201 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23202     /**
23203      * @cfg {Roo.data.Store} dataSource
23204      * The underlying data store providing the paged data
23205      */
23206     /**
23207      * @cfg {String/HTMLElement/Element} container
23208      * container The id or element that will contain the toolbar
23209      */
23210     /**
23211      * @cfg {Boolean} displayInfo
23212      * True to display the displayMsg (defaults to false)
23213      */
23214     /**
23215      * @cfg {Number} pageSize
23216      * The number of records to display per page (defaults to 20)
23217      */
23218     pageSize: 20,
23219     /**
23220      * @cfg {String} displayMsg
23221      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23222      */
23223     displayMsg : 'Displaying {0} - {1} of {2}',
23224     /**
23225      * @cfg {String} emptyMsg
23226      * The message to display when no records are found (defaults to "No data to display")
23227      */
23228     emptyMsg : 'No data to display',
23229     /**
23230      * Customizable piece of the default paging text (defaults to "Page")
23231      * @type String
23232      */
23233     beforePageText : "Page",
23234     /**
23235      * Customizable piece of the default paging text (defaults to "of %0")
23236      * @type String
23237      */
23238     afterPageText : "of {0}",
23239     /**
23240      * Customizable piece of the default paging text (defaults to "First Page")
23241      * @type String
23242      */
23243     firstText : "First Page",
23244     /**
23245      * Customizable piece of the default paging text (defaults to "Previous Page")
23246      * @type String
23247      */
23248     prevText : "Previous Page",
23249     /**
23250      * Customizable piece of the default paging text (defaults to "Next Page")
23251      * @type String
23252      */
23253     nextText : "Next Page",
23254     /**
23255      * Customizable piece of the default paging text (defaults to "Last Page")
23256      * @type String
23257      */
23258     lastText : "Last Page",
23259     /**
23260      * Customizable piece of the default paging text (defaults to "Refresh")
23261      * @type String
23262      */
23263     refreshText : "Refresh",
23264
23265     buttons : false,
23266     // private
23267     onRender : function(ct, position) 
23268     {
23269         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23270         this.navgroup.parentId = this.id;
23271         this.navgroup.onRender(this.el, null);
23272         // add the buttons to the navgroup
23273         
23274         if(this.displayInfo){
23275             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23276             this.displayEl = this.el.select('.x-paging-info', true).first();
23277 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23278 //            this.displayEl = navel.el.select('span',true).first();
23279         }
23280         
23281         var _this = this;
23282         
23283         if(this.buttons){
23284             Roo.each(_this.buttons, function(e){ // this might need to use render????
23285                Roo.factory(e).onRender(_this.el, null);
23286             });
23287         }
23288             
23289         Roo.each(_this.toolbarItems, function(e) {
23290             _this.navgroup.addItem(e);
23291         });
23292         
23293         
23294         this.first = this.navgroup.addItem({
23295             tooltip: this.firstText,
23296             cls: "prev",
23297             icon : 'fa fa-backward',
23298             disabled: true,
23299             preventDefault: true,
23300             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23301         });
23302         
23303         this.prev =  this.navgroup.addItem({
23304             tooltip: this.prevText,
23305             cls: "prev",
23306             icon : 'fa fa-step-backward',
23307             disabled: true,
23308             preventDefault: true,
23309             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23310         });
23311     //this.addSeparator();
23312         
23313         
23314         var field = this.navgroup.addItem( {
23315             tagtype : 'span',
23316             cls : 'x-paging-position',
23317             
23318             html : this.beforePageText  +
23319                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23320                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23321          } ); //?? escaped?
23322         
23323         this.field = field.el.select('input', true).first();
23324         this.field.on("keydown", this.onPagingKeydown, this);
23325         this.field.on("focus", function(){this.dom.select();});
23326     
23327     
23328         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23329         //this.field.setHeight(18);
23330         //this.addSeparator();
23331         this.next = this.navgroup.addItem({
23332             tooltip: this.nextText,
23333             cls: "next",
23334             html : ' <i class="fa fa-step-forward">',
23335             disabled: true,
23336             preventDefault: true,
23337             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23338         });
23339         this.last = this.navgroup.addItem({
23340             tooltip: this.lastText,
23341             icon : 'fa fa-forward',
23342             cls: "next",
23343             disabled: true,
23344             preventDefault: true,
23345             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23346         });
23347     //this.addSeparator();
23348         this.loading = this.navgroup.addItem({
23349             tooltip: this.refreshText,
23350             icon: 'fa fa-refresh',
23351             preventDefault: true,
23352             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23353         });
23354         
23355     },
23356
23357     // private
23358     updateInfo : function(){
23359         if(this.displayEl){
23360             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23361             var msg = count == 0 ?
23362                 this.emptyMsg :
23363                 String.format(
23364                     this.displayMsg,
23365                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23366                 );
23367             this.displayEl.update(msg);
23368         }
23369     },
23370
23371     // private
23372     onLoad : function(ds, r, o){
23373        this.cursor = o.params ? o.params.start : 0;
23374        var d = this.getPageData(),
23375             ap = d.activePage,
23376             ps = d.pages;
23377         
23378        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23379        this.field.dom.value = ap;
23380        this.first.setDisabled(ap == 1);
23381        this.prev.setDisabled(ap == 1);
23382        this.next.setDisabled(ap == ps);
23383        this.last.setDisabled(ap == ps);
23384        this.loading.enable();
23385        this.updateInfo();
23386     },
23387
23388     // private
23389     getPageData : function(){
23390         var total = this.ds.getTotalCount();
23391         return {
23392             total : total,
23393             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23394             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23395         };
23396     },
23397
23398     // private
23399     onLoadError : function(){
23400         this.loading.enable();
23401     },
23402
23403     // private
23404     onPagingKeydown : function(e){
23405         var k = e.getKey();
23406         var d = this.getPageData();
23407         if(k == e.RETURN){
23408             var v = this.field.dom.value, pageNum;
23409             if(!v || isNaN(pageNum = parseInt(v, 10))){
23410                 this.field.dom.value = d.activePage;
23411                 return;
23412             }
23413             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23414             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23415             e.stopEvent();
23416         }
23417         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))
23418         {
23419           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23420           this.field.dom.value = pageNum;
23421           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23422           e.stopEvent();
23423         }
23424         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23425         {
23426           var v = this.field.dom.value, pageNum; 
23427           var increment = (e.shiftKey) ? 10 : 1;
23428           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23429                 increment *= -1;
23430           }
23431           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23432             this.field.dom.value = d.activePage;
23433             return;
23434           }
23435           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23436           {
23437             this.field.dom.value = parseInt(v, 10) + increment;
23438             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23439             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23440           }
23441           e.stopEvent();
23442         }
23443     },
23444
23445     // private
23446     beforeLoad : function(){
23447         if(this.loading){
23448             this.loading.disable();
23449         }
23450     },
23451
23452     // private
23453     onClick : function(which){
23454         
23455         var ds = this.ds;
23456         if (!ds) {
23457             return;
23458         }
23459         
23460         switch(which){
23461             case "first":
23462                 ds.load({params:{start: 0, limit: this.pageSize}});
23463             break;
23464             case "prev":
23465                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23466             break;
23467             case "next":
23468                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23469             break;
23470             case "last":
23471                 var total = ds.getTotalCount();
23472                 var extra = total % this.pageSize;
23473                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23474                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23475             break;
23476             case "refresh":
23477                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23478             break;
23479         }
23480     },
23481
23482     /**
23483      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23484      * @param {Roo.data.Store} store The data store to unbind
23485      */
23486     unbind : function(ds){
23487         ds.un("beforeload", this.beforeLoad, this);
23488         ds.un("load", this.onLoad, this);
23489         ds.un("loadexception", this.onLoadError, this);
23490         ds.un("remove", this.updateInfo, this);
23491         ds.un("add", this.updateInfo, this);
23492         this.ds = undefined;
23493     },
23494
23495     /**
23496      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23497      * @param {Roo.data.Store} store The data store to bind
23498      */
23499     bind : function(ds){
23500         ds.on("beforeload", this.beforeLoad, this);
23501         ds.on("load", this.onLoad, this);
23502         ds.on("loadexception", this.onLoadError, this);
23503         ds.on("remove", this.updateInfo, this);
23504         ds.on("add", this.updateInfo, this);
23505         this.ds = ds;
23506     }
23507 });/*
23508  * - LGPL
23509  *
23510  * element
23511  * 
23512  */
23513
23514 /**
23515  * @class Roo.bootstrap.MessageBar
23516  * @extends Roo.bootstrap.Component
23517  * Bootstrap MessageBar class
23518  * @cfg {String} html contents of the MessageBar
23519  * @cfg {String} weight (info | success | warning | danger) default info
23520  * @cfg {String} beforeClass insert the bar before the given class
23521  * @cfg {Boolean} closable (true | false) default false
23522  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23523  * 
23524  * @constructor
23525  * Create a new Element
23526  * @param {Object} config The config object
23527  */
23528
23529 Roo.bootstrap.MessageBar = function(config){
23530     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23531 };
23532
23533 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23534     
23535     html: '',
23536     weight: 'info',
23537     closable: false,
23538     fixed: false,
23539     beforeClass: 'bootstrap-sticky-wrap',
23540     
23541     getAutoCreate : function(){
23542         
23543         var cfg = {
23544             tag: 'div',
23545             cls: 'alert alert-dismissable alert-' + this.weight,
23546             cn: [
23547                 {
23548                     tag: 'span',
23549                     cls: 'message',
23550                     html: this.html || ''
23551                 }
23552             ]
23553         };
23554         
23555         if(this.fixed){
23556             cfg.cls += ' alert-messages-fixed';
23557         }
23558         
23559         if(this.closable){
23560             cfg.cn.push({
23561                 tag: 'button',
23562                 cls: 'close',
23563                 html: 'x'
23564             });
23565         }
23566         
23567         return cfg;
23568     },
23569     
23570     onRender : function(ct, position)
23571     {
23572         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23573         
23574         if(!this.el){
23575             var cfg = Roo.apply({},  this.getAutoCreate());
23576             cfg.id = Roo.id();
23577             
23578             if (this.cls) {
23579                 cfg.cls += ' ' + this.cls;
23580             }
23581             if (this.style) {
23582                 cfg.style = this.style;
23583             }
23584             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23585             
23586             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23587         }
23588         
23589         this.el.select('>button.close').on('click', this.hide, this);
23590         
23591     },
23592     
23593     show : function()
23594     {
23595         if (!this.rendered) {
23596             this.render();
23597         }
23598         
23599         this.el.show();
23600         
23601         this.fireEvent('show', this);
23602         
23603     },
23604     
23605     hide : function()
23606     {
23607         if (!this.rendered) {
23608             this.render();
23609         }
23610         
23611         this.el.hide();
23612         
23613         this.fireEvent('hide', this);
23614     },
23615     
23616     update : function()
23617     {
23618 //        var e = this.el.dom.firstChild;
23619 //        
23620 //        if(this.closable){
23621 //            e = e.nextSibling;
23622 //        }
23623 //        
23624 //        e.data = this.html || '';
23625
23626         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23627     }
23628    
23629 });
23630
23631  
23632
23633      /*
23634  * - LGPL
23635  *
23636  * Graph
23637  * 
23638  */
23639
23640
23641 /**
23642  * @class Roo.bootstrap.Graph
23643  * @extends Roo.bootstrap.Component
23644  * Bootstrap Graph class
23645 > Prameters
23646  -sm {number} sm 4
23647  -md {number} md 5
23648  @cfg {String} graphtype  bar | vbar | pie
23649  @cfg {number} g_x coodinator | centre x (pie)
23650  @cfg {number} g_y coodinator | centre y (pie)
23651  @cfg {number} g_r radius (pie)
23652  @cfg {number} g_height height of the chart (respected by all elements in the set)
23653  @cfg {number} g_width width of the chart (respected by all elements in the set)
23654  @cfg {Object} title The title of the chart
23655     
23656  -{Array}  values
23657  -opts (object) options for the chart 
23658      o {
23659      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23660      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23661      o vgutter (number)
23662      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.
23663      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23664      o to
23665      o stretch (boolean)
23666      o }
23667  -opts (object) options for the pie
23668      o{
23669      o cut
23670      o startAngle (number)
23671      o endAngle (number)
23672      } 
23673  *
23674  * @constructor
23675  * Create a new Input
23676  * @param {Object} config The config object
23677  */
23678
23679 Roo.bootstrap.Graph = function(config){
23680     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23681     
23682     this.addEvents({
23683         // img events
23684         /**
23685          * @event click
23686          * The img click event for the img.
23687          * @param {Roo.EventObject} e
23688          */
23689         "click" : true
23690     });
23691 };
23692
23693 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23694     
23695     sm: 4,
23696     md: 5,
23697     graphtype: 'bar',
23698     g_height: 250,
23699     g_width: 400,
23700     g_x: 50,
23701     g_y: 50,
23702     g_r: 30,
23703     opts:{
23704         //g_colors: this.colors,
23705         g_type: 'soft',
23706         g_gutter: '20%'
23707
23708     },
23709     title : false,
23710
23711     getAutoCreate : function(){
23712         
23713         var cfg = {
23714             tag: 'div',
23715             html : null
23716         };
23717         
23718         
23719         return  cfg;
23720     },
23721
23722     onRender : function(ct,position){
23723         
23724         
23725         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23726         
23727         if (typeof(Raphael) == 'undefined') {
23728             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23729             return;
23730         }
23731         
23732         this.raphael = Raphael(this.el.dom);
23733         
23734                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23735                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23736                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23737                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23738                 /*
23739                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23740                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23741                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23742                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23743                 
23744                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23745                 r.barchart(330, 10, 300, 220, data1);
23746                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23747                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23748                 */
23749                 
23750                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23751                 // r.barchart(30, 30, 560, 250,  xdata, {
23752                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23753                 //     axis : "0 0 1 1",
23754                 //     axisxlabels :  xdata
23755                 //     //yvalues : cols,
23756                    
23757                 // });
23758 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23759 //        
23760 //        this.load(null,xdata,{
23761 //                axis : "0 0 1 1",
23762 //                axisxlabels :  xdata
23763 //                });
23764
23765     },
23766
23767     load : function(graphtype,xdata,opts)
23768     {
23769         this.raphael.clear();
23770         if(!graphtype) {
23771             graphtype = this.graphtype;
23772         }
23773         if(!opts){
23774             opts = this.opts;
23775         }
23776         var r = this.raphael,
23777             fin = function () {
23778                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23779             },
23780             fout = function () {
23781                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23782             },
23783             pfin = function() {
23784                 this.sector.stop();
23785                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23786
23787                 if (this.label) {
23788                     this.label[0].stop();
23789                     this.label[0].attr({ r: 7.5 });
23790                     this.label[1].attr({ "font-weight": 800 });
23791                 }
23792             },
23793             pfout = function() {
23794                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23795
23796                 if (this.label) {
23797                     this.label[0].animate({ r: 5 }, 500, "bounce");
23798                     this.label[1].attr({ "font-weight": 400 });
23799                 }
23800             };
23801
23802         switch(graphtype){
23803             case 'bar':
23804                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23805                 break;
23806             case 'hbar':
23807                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23808                 break;
23809             case 'pie':
23810 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23811 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23812 //            
23813                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23814                 
23815                 break;
23816
23817         }
23818         
23819         if(this.title){
23820             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23821         }
23822         
23823     },
23824     
23825     setTitle: function(o)
23826     {
23827         this.title = o;
23828     },
23829     
23830     initEvents: function() {
23831         
23832         if(!this.href){
23833             this.el.on('click', this.onClick, this);
23834         }
23835     },
23836     
23837     onClick : function(e)
23838     {
23839         Roo.log('img onclick');
23840         this.fireEvent('click', this, e);
23841     }
23842    
23843 });
23844
23845  
23846 /*
23847  * - LGPL
23848  *
23849  * numberBox
23850  * 
23851  */
23852 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23853
23854 /**
23855  * @class Roo.bootstrap.dash.NumberBox
23856  * @extends Roo.bootstrap.Component
23857  * Bootstrap NumberBox class
23858  * @cfg {String} headline Box headline
23859  * @cfg {String} content Box content
23860  * @cfg {String} icon Box icon
23861  * @cfg {String} footer Footer text
23862  * @cfg {String} fhref Footer href
23863  * 
23864  * @constructor
23865  * Create a new NumberBox
23866  * @param {Object} config The config object
23867  */
23868
23869
23870 Roo.bootstrap.dash.NumberBox = function(config){
23871     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23872     
23873 };
23874
23875 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23876     
23877     headline : '',
23878     content : '',
23879     icon : '',
23880     footer : '',
23881     fhref : '',
23882     ficon : '',
23883     
23884     getAutoCreate : function(){
23885         
23886         var cfg = {
23887             tag : 'div',
23888             cls : 'small-box ',
23889             cn : [
23890                 {
23891                     tag : 'div',
23892                     cls : 'inner',
23893                     cn :[
23894                         {
23895                             tag : 'h3',
23896                             cls : 'roo-headline',
23897                             html : this.headline
23898                         },
23899                         {
23900                             tag : 'p',
23901                             cls : 'roo-content',
23902                             html : this.content
23903                         }
23904                     ]
23905                 }
23906             ]
23907         };
23908         
23909         if(this.icon){
23910             cfg.cn.push({
23911                 tag : 'div',
23912                 cls : 'icon',
23913                 cn :[
23914                     {
23915                         tag : 'i',
23916                         cls : 'ion ' + this.icon
23917                     }
23918                 ]
23919             });
23920         }
23921         
23922         if(this.footer){
23923             var footer = {
23924                 tag : 'a',
23925                 cls : 'small-box-footer',
23926                 href : this.fhref || '#',
23927                 html : this.footer
23928             };
23929             
23930             cfg.cn.push(footer);
23931             
23932         }
23933         
23934         return  cfg;
23935     },
23936
23937     onRender : function(ct,position){
23938         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23939
23940
23941        
23942                 
23943     },
23944
23945     setHeadline: function (value)
23946     {
23947         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23948     },
23949     
23950     setFooter: function (value, href)
23951     {
23952         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23953         
23954         if(href){
23955             this.el.select('a.small-box-footer',true).first().attr('href', href);
23956         }
23957         
23958     },
23959
23960     setContent: function (value)
23961     {
23962         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23963     },
23964
23965     initEvents: function() 
23966     {   
23967         
23968     }
23969     
23970 });
23971
23972  
23973 /*
23974  * - LGPL
23975  *
23976  * TabBox
23977  * 
23978  */
23979 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23980
23981 /**
23982  * @class Roo.bootstrap.dash.TabBox
23983  * @extends Roo.bootstrap.Component
23984  * Bootstrap TabBox class
23985  * @cfg {String} title Title of the TabBox
23986  * @cfg {String} icon Icon of the TabBox
23987  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23988  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23989  * 
23990  * @constructor
23991  * Create a new TabBox
23992  * @param {Object} config The config object
23993  */
23994
23995
23996 Roo.bootstrap.dash.TabBox = function(config){
23997     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23998     this.addEvents({
23999         // raw events
24000         /**
24001          * @event addpane
24002          * When a pane is added
24003          * @param {Roo.bootstrap.dash.TabPane} pane
24004          */
24005         "addpane" : true,
24006         /**
24007          * @event activatepane
24008          * When a pane is activated
24009          * @param {Roo.bootstrap.dash.TabPane} pane
24010          */
24011         "activatepane" : true
24012         
24013          
24014     });
24015     
24016     this.panes = [];
24017 };
24018
24019 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24020
24021     title : '',
24022     icon : false,
24023     showtabs : true,
24024     tabScrollable : false,
24025     
24026     getChildContainer : function()
24027     {
24028         return this.el.select('.tab-content', true).first();
24029     },
24030     
24031     getAutoCreate : function(){
24032         
24033         var header = {
24034             tag: 'li',
24035             cls: 'pull-left header',
24036             html: this.title,
24037             cn : []
24038         };
24039         
24040         if(this.icon){
24041             header.cn.push({
24042                 tag: 'i',
24043                 cls: 'fa ' + this.icon
24044             });
24045         }
24046         
24047         var h = {
24048             tag: 'ul',
24049             cls: 'nav nav-tabs pull-right',
24050             cn: [
24051                 header
24052             ]
24053         };
24054         
24055         if(this.tabScrollable){
24056             h = {
24057                 tag: 'div',
24058                 cls: 'tab-header',
24059                 cn: [
24060                     {
24061                         tag: 'ul',
24062                         cls: 'nav nav-tabs pull-right',
24063                         cn: [
24064                             header
24065                         ]
24066                     }
24067                 ]
24068             };
24069         }
24070         
24071         var cfg = {
24072             tag: 'div',
24073             cls: 'nav-tabs-custom',
24074             cn: [
24075                 h,
24076                 {
24077                     tag: 'div',
24078                     cls: 'tab-content no-padding',
24079                     cn: []
24080                 }
24081             ]
24082         };
24083
24084         return  cfg;
24085     },
24086     initEvents : function()
24087     {
24088         //Roo.log('add add pane handler');
24089         this.on('addpane', this.onAddPane, this);
24090     },
24091      /**
24092      * Updates the box title
24093      * @param {String} html to set the title to.
24094      */
24095     setTitle : function(value)
24096     {
24097         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24098     },
24099     onAddPane : function(pane)
24100     {
24101         this.panes.push(pane);
24102         //Roo.log('addpane');
24103         //Roo.log(pane);
24104         // tabs are rendere left to right..
24105         if(!this.showtabs){
24106             return;
24107         }
24108         
24109         var ctr = this.el.select('.nav-tabs', true).first();
24110          
24111          
24112         var existing = ctr.select('.nav-tab',true);
24113         var qty = existing.getCount();;
24114         
24115         
24116         var tab = ctr.createChild({
24117             tag : 'li',
24118             cls : 'nav-tab' + (qty ? '' : ' active'),
24119             cn : [
24120                 {
24121                     tag : 'a',
24122                     href:'#',
24123                     html : pane.title
24124                 }
24125             ]
24126         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24127         pane.tab = tab;
24128         
24129         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24130         if (!qty) {
24131             pane.el.addClass('active');
24132         }
24133         
24134                 
24135     },
24136     onTabClick : function(ev,un,ob,pane)
24137     {
24138         //Roo.log('tab - prev default');
24139         ev.preventDefault();
24140         
24141         
24142         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24143         pane.tab.addClass('active');
24144         //Roo.log(pane.title);
24145         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24146         // technically we should have a deactivate event.. but maybe add later.
24147         // and it should not de-activate the selected tab...
24148         this.fireEvent('activatepane', pane);
24149         pane.el.addClass('active');
24150         pane.fireEvent('activate');
24151         
24152         
24153     },
24154     
24155     getActivePane : function()
24156     {
24157         var r = false;
24158         Roo.each(this.panes, function(p) {
24159             if(p.el.hasClass('active')){
24160                 r = p;
24161                 return false;
24162             }
24163             
24164             return;
24165         });
24166         
24167         return r;
24168     }
24169     
24170     
24171 });
24172
24173  
24174 /*
24175  * - LGPL
24176  *
24177  * Tab pane
24178  * 
24179  */
24180 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24181 /**
24182  * @class Roo.bootstrap.TabPane
24183  * @extends Roo.bootstrap.Component
24184  * Bootstrap TabPane class
24185  * @cfg {Boolean} active (false | true) Default false
24186  * @cfg {String} title title of panel
24187
24188  * 
24189  * @constructor
24190  * Create a new TabPane
24191  * @param {Object} config The config object
24192  */
24193
24194 Roo.bootstrap.dash.TabPane = function(config){
24195     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24196     
24197     this.addEvents({
24198         // raw events
24199         /**
24200          * @event activate
24201          * When a pane is activated
24202          * @param {Roo.bootstrap.dash.TabPane} pane
24203          */
24204         "activate" : true
24205          
24206     });
24207 };
24208
24209 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24210     
24211     active : false,
24212     title : '',
24213     
24214     // the tabBox that this is attached to.
24215     tab : false,
24216      
24217     getAutoCreate : function() 
24218     {
24219         var cfg = {
24220             tag: 'div',
24221             cls: 'tab-pane'
24222         };
24223         
24224         if(this.active){
24225             cfg.cls += ' active';
24226         }
24227         
24228         return cfg;
24229     },
24230     initEvents  : function()
24231     {
24232         //Roo.log('trigger add pane handler');
24233         this.parent().fireEvent('addpane', this)
24234     },
24235     
24236      /**
24237      * Updates the tab title 
24238      * @param {String} html to set the title to.
24239      */
24240     setTitle: function(str)
24241     {
24242         if (!this.tab) {
24243             return;
24244         }
24245         this.title = str;
24246         this.tab.select('a', true).first().dom.innerHTML = str;
24247         
24248     }
24249     
24250     
24251     
24252 });
24253
24254  
24255
24256
24257  /*
24258  * - LGPL
24259  *
24260  * menu
24261  * 
24262  */
24263 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24264
24265 /**
24266  * @class Roo.bootstrap.menu.Menu
24267  * @extends Roo.bootstrap.Component
24268  * Bootstrap Menu class - container for Menu
24269  * @cfg {String} html Text of the menu
24270  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24271  * @cfg {String} icon Font awesome icon
24272  * @cfg {String} pos Menu align to (top | bottom) default bottom
24273  * 
24274  * 
24275  * @constructor
24276  * Create a new Menu
24277  * @param {Object} config The config object
24278  */
24279
24280
24281 Roo.bootstrap.menu.Menu = function(config){
24282     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24283     
24284     this.addEvents({
24285         /**
24286          * @event beforeshow
24287          * Fires before this menu is displayed
24288          * @param {Roo.bootstrap.menu.Menu} this
24289          */
24290         beforeshow : true,
24291         /**
24292          * @event beforehide
24293          * Fires before this menu is hidden
24294          * @param {Roo.bootstrap.menu.Menu} this
24295          */
24296         beforehide : true,
24297         /**
24298          * @event show
24299          * Fires after this menu is displayed
24300          * @param {Roo.bootstrap.menu.Menu} this
24301          */
24302         show : true,
24303         /**
24304          * @event hide
24305          * Fires after this menu is hidden
24306          * @param {Roo.bootstrap.menu.Menu} this
24307          */
24308         hide : true,
24309         /**
24310          * @event click
24311          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24312          * @param {Roo.bootstrap.menu.Menu} this
24313          * @param {Roo.EventObject} e
24314          */
24315         click : true
24316     });
24317     
24318 };
24319
24320 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24321     
24322     submenu : false,
24323     html : '',
24324     weight : 'default',
24325     icon : false,
24326     pos : 'bottom',
24327     
24328     
24329     getChildContainer : function() {
24330         if(this.isSubMenu){
24331             return this.el;
24332         }
24333         
24334         return this.el.select('ul.dropdown-menu', true).first();  
24335     },
24336     
24337     getAutoCreate : function()
24338     {
24339         var text = [
24340             {
24341                 tag : 'span',
24342                 cls : 'roo-menu-text',
24343                 html : this.html
24344             }
24345         ];
24346         
24347         if(this.icon){
24348             text.unshift({
24349                 tag : 'i',
24350                 cls : 'fa ' + this.icon
24351             })
24352         }
24353         
24354         
24355         var cfg = {
24356             tag : 'div',
24357             cls : 'btn-group',
24358             cn : [
24359                 {
24360                     tag : 'button',
24361                     cls : 'dropdown-button btn btn-' + this.weight,
24362                     cn : text
24363                 },
24364                 {
24365                     tag : 'button',
24366                     cls : 'dropdown-toggle btn btn-' + this.weight,
24367                     cn : [
24368                         {
24369                             tag : 'span',
24370                             cls : 'caret'
24371                         }
24372                     ]
24373                 },
24374                 {
24375                     tag : 'ul',
24376                     cls : 'dropdown-menu'
24377                 }
24378             ]
24379             
24380         };
24381         
24382         if(this.pos == 'top'){
24383             cfg.cls += ' dropup';
24384         }
24385         
24386         if(this.isSubMenu){
24387             cfg = {
24388                 tag : 'ul',
24389                 cls : 'dropdown-menu'
24390             }
24391         }
24392         
24393         return cfg;
24394     },
24395     
24396     onRender : function(ct, position)
24397     {
24398         this.isSubMenu = ct.hasClass('dropdown-submenu');
24399         
24400         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24401     },
24402     
24403     initEvents : function() 
24404     {
24405         if(this.isSubMenu){
24406             return;
24407         }
24408         
24409         this.hidden = true;
24410         
24411         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24412         this.triggerEl.on('click', this.onTriggerPress, this);
24413         
24414         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24415         this.buttonEl.on('click', this.onClick, this);
24416         
24417     },
24418     
24419     list : function()
24420     {
24421         if(this.isSubMenu){
24422             return this.el;
24423         }
24424         
24425         return this.el.select('ul.dropdown-menu', true).first();
24426     },
24427     
24428     onClick : function(e)
24429     {
24430         this.fireEvent("click", this, e);
24431     },
24432     
24433     onTriggerPress  : function(e)
24434     {   
24435         if (this.isVisible()) {
24436             this.hide();
24437         } else {
24438             this.show();
24439         }
24440     },
24441     
24442     isVisible : function(){
24443         return !this.hidden;
24444     },
24445     
24446     show : function()
24447     {
24448         this.fireEvent("beforeshow", this);
24449         
24450         this.hidden = false;
24451         this.el.addClass('open');
24452         
24453         Roo.get(document).on("mouseup", this.onMouseUp, this);
24454         
24455         this.fireEvent("show", this);
24456         
24457         
24458     },
24459     
24460     hide : function()
24461     {
24462         this.fireEvent("beforehide", this);
24463         
24464         this.hidden = true;
24465         this.el.removeClass('open');
24466         
24467         Roo.get(document).un("mouseup", this.onMouseUp);
24468         
24469         this.fireEvent("hide", this);
24470     },
24471     
24472     onMouseUp : function()
24473     {
24474         this.hide();
24475     }
24476     
24477 });
24478
24479  
24480  /*
24481  * - LGPL
24482  *
24483  * menu item
24484  * 
24485  */
24486 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24487
24488 /**
24489  * @class Roo.bootstrap.menu.Item
24490  * @extends Roo.bootstrap.Component
24491  * Bootstrap MenuItem class
24492  * @cfg {Boolean} submenu (true | false) default false
24493  * @cfg {String} html text of the item
24494  * @cfg {String} href the link
24495  * @cfg {Boolean} disable (true | false) default false
24496  * @cfg {Boolean} preventDefault (true | false) default true
24497  * @cfg {String} icon Font awesome icon
24498  * @cfg {String} pos Submenu align to (left | right) default right 
24499  * 
24500  * 
24501  * @constructor
24502  * Create a new Item
24503  * @param {Object} config The config object
24504  */
24505
24506
24507 Roo.bootstrap.menu.Item = function(config){
24508     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24509     this.addEvents({
24510         /**
24511          * @event mouseover
24512          * Fires when the mouse is hovering over this menu
24513          * @param {Roo.bootstrap.menu.Item} this
24514          * @param {Roo.EventObject} e
24515          */
24516         mouseover : true,
24517         /**
24518          * @event mouseout
24519          * Fires when the mouse exits this menu
24520          * @param {Roo.bootstrap.menu.Item} this
24521          * @param {Roo.EventObject} e
24522          */
24523         mouseout : true,
24524         // raw events
24525         /**
24526          * @event click
24527          * The raw click event for the entire grid.
24528          * @param {Roo.EventObject} e
24529          */
24530         click : true
24531     });
24532 };
24533
24534 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24535     
24536     submenu : false,
24537     href : '',
24538     html : '',
24539     preventDefault: true,
24540     disable : false,
24541     icon : false,
24542     pos : 'right',
24543     
24544     getAutoCreate : function()
24545     {
24546         var text = [
24547             {
24548                 tag : 'span',
24549                 cls : 'roo-menu-item-text',
24550                 html : this.html
24551             }
24552         ];
24553         
24554         if(this.icon){
24555             text.unshift({
24556                 tag : 'i',
24557                 cls : 'fa ' + this.icon
24558             })
24559         }
24560         
24561         var cfg = {
24562             tag : 'li',
24563             cn : [
24564                 {
24565                     tag : 'a',
24566                     href : this.href || '#',
24567                     cn : text
24568                 }
24569             ]
24570         };
24571         
24572         if(this.disable){
24573             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24574         }
24575         
24576         if(this.submenu){
24577             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24578             
24579             if(this.pos == 'left'){
24580                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24581             }
24582         }
24583         
24584         return cfg;
24585     },
24586     
24587     initEvents : function() 
24588     {
24589         this.el.on('mouseover', this.onMouseOver, this);
24590         this.el.on('mouseout', this.onMouseOut, this);
24591         
24592         this.el.select('a', true).first().on('click', this.onClick, this);
24593         
24594     },
24595     
24596     onClick : function(e)
24597     {
24598         if(this.preventDefault){
24599             e.preventDefault();
24600         }
24601         
24602         this.fireEvent("click", this, e);
24603     },
24604     
24605     onMouseOver : function(e)
24606     {
24607         if(this.submenu && this.pos == 'left'){
24608             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24609         }
24610         
24611         this.fireEvent("mouseover", this, e);
24612     },
24613     
24614     onMouseOut : function(e)
24615     {
24616         this.fireEvent("mouseout", this, e);
24617     }
24618 });
24619
24620  
24621
24622  /*
24623  * - LGPL
24624  *
24625  * menu separator
24626  * 
24627  */
24628 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24629
24630 /**
24631  * @class Roo.bootstrap.menu.Separator
24632  * @extends Roo.bootstrap.Component
24633  * Bootstrap Separator class
24634  * 
24635  * @constructor
24636  * Create a new Separator
24637  * @param {Object} config The config object
24638  */
24639
24640
24641 Roo.bootstrap.menu.Separator = function(config){
24642     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24643 };
24644
24645 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24646     
24647     getAutoCreate : function(){
24648         var cfg = {
24649             tag : 'li',
24650             cls: 'divider'
24651         };
24652         
24653         return cfg;
24654     }
24655    
24656 });
24657
24658  
24659
24660  /*
24661  * - LGPL
24662  *
24663  * Tooltip
24664  * 
24665  */
24666
24667 /**
24668  * @class Roo.bootstrap.Tooltip
24669  * Bootstrap Tooltip class
24670  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24671  * to determine which dom element triggers the tooltip.
24672  * 
24673  * It needs to add support for additional attributes like tooltip-position
24674  * 
24675  * @constructor
24676  * Create a new Toolti
24677  * @param {Object} config The config object
24678  */
24679
24680 Roo.bootstrap.Tooltip = function(config){
24681     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24682 };
24683
24684 Roo.apply(Roo.bootstrap.Tooltip, {
24685     /**
24686      * @function init initialize tooltip monitoring.
24687      * @static
24688      */
24689     currentEl : false,
24690     currentTip : false,
24691     currentRegion : false,
24692     
24693     //  init : delay?
24694     
24695     init : function()
24696     {
24697         Roo.get(document).on('mouseover', this.enter ,this);
24698         Roo.get(document).on('mouseout', this.leave, this);
24699          
24700         
24701         this.currentTip = new Roo.bootstrap.Tooltip();
24702     },
24703     
24704     enter : function(ev)
24705     {
24706         var dom = ev.getTarget();
24707         
24708         //Roo.log(['enter',dom]);
24709         var el = Roo.fly(dom);
24710         if (this.currentEl) {
24711             //Roo.log(dom);
24712             //Roo.log(this.currentEl);
24713             //Roo.log(this.currentEl.contains(dom));
24714             if (this.currentEl == el) {
24715                 return;
24716             }
24717             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24718                 return;
24719             }
24720
24721         }
24722         
24723         if (this.currentTip.el) {
24724             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24725         }    
24726         //Roo.log(ev);
24727         
24728         if(!el || el.dom == document){
24729             return;
24730         }
24731         
24732         var bindEl = el;
24733         
24734         // you can not look for children, as if el is the body.. then everythign is the child..
24735         if (!el.attr('tooltip')) { //
24736             if (!el.select("[tooltip]").elements.length) {
24737                 return;
24738             }
24739             // is the mouse over this child...?
24740             bindEl = el.select("[tooltip]").first();
24741             var xy = ev.getXY();
24742             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24743                 //Roo.log("not in region.");
24744                 return;
24745             }
24746             //Roo.log("child element over..");
24747             
24748         }
24749         this.currentEl = bindEl;
24750         this.currentTip.bind(bindEl);
24751         this.currentRegion = Roo.lib.Region.getRegion(dom);
24752         this.currentTip.enter();
24753         
24754     },
24755     leave : function(ev)
24756     {
24757         var dom = ev.getTarget();
24758         //Roo.log(['leave',dom]);
24759         if (!this.currentEl) {
24760             return;
24761         }
24762         
24763         
24764         if (dom != this.currentEl.dom) {
24765             return;
24766         }
24767         var xy = ev.getXY();
24768         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24769             return;
24770         }
24771         // only activate leave if mouse cursor is outside... bounding box..
24772         
24773         
24774         
24775         
24776         if (this.currentTip) {
24777             this.currentTip.leave();
24778         }
24779         //Roo.log('clear currentEl');
24780         this.currentEl = false;
24781         
24782         
24783     },
24784     alignment : {
24785         'left' : ['r-l', [-2,0], 'right'],
24786         'right' : ['l-r', [2,0], 'left'],
24787         'bottom' : ['t-b', [0,2], 'top'],
24788         'top' : [ 'b-t', [0,-2], 'bottom']
24789     }
24790     
24791 });
24792
24793
24794 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24795     
24796     
24797     bindEl : false,
24798     
24799     delay : null, // can be { show : 300 , hide: 500}
24800     
24801     timeout : null,
24802     
24803     hoverState : null, //???
24804     
24805     placement : 'bottom', 
24806     
24807     getAutoCreate : function(){
24808     
24809         var cfg = {
24810            cls : 'tooltip',
24811            role : 'tooltip',
24812            cn : [
24813                 {
24814                     cls : 'tooltip-arrow'
24815                 },
24816                 {
24817                     cls : 'tooltip-inner'
24818                 }
24819            ]
24820         };
24821         
24822         return cfg;
24823     },
24824     bind : function(el)
24825     {
24826         this.bindEl = el;
24827     },
24828       
24829     
24830     enter : function () {
24831        
24832         if (this.timeout != null) {
24833             clearTimeout(this.timeout);
24834         }
24835         
24836         this.hoverState = 'in';
24837          //Roo.log("enter - show");
24838         if (!this.delay || !this.delay.show) {
24839             this.show();
24840             return;
24841         }
24842         var _t = this;
24843         this.timeout = setTimeout(function () {
24844             if (_t.hoverState == 'in') {
24845                 _t.show();
24846             }
24847         }, this.delay.show);
24848     },
24849     leave : function()
24850     {
24851         clearTimeout(this.timeout);
24852     
24853         this.hoverState = 'out';
24854          if (!this.delay || !this.delay.hide) {
24855             this.hide();
24856             return;
24857         }
24858        
24859         var _t = this;
24860         this.timeout = setTimeout(function () {
24861             //Roo.log("leave - timeout");
24862             
24863             if (_t.hoverState == 'out') {
24864                 _t.hide();
24865                 Roo.bootstrap.Tooltip.currentEl = false;
24866             }
24867         }, delay);
24868     },
24869     
24870     show : function ()
24871     {
24872         if (!this.el) {
24873             this.render(document.body);
24874         }
24875         // set content.
24876         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24877         
24878         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24879         
24880         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24881         
24882         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24883         
24884         var placement = typeof this.placement == 'function' ?
24885             this.placement.call(this, this.el, on_el) :
24886             this.placement;
24887             
24888         var autoToken = /\s?auto?\s?/i;
24889         var autoPlace = autoToken.test(placement);
24890         if (autoPlace) {
24891             placement = placement.replace(autoToken, '') || 'top';
24892         }
24893         
24894         //this.el.detach()
24895         //this.el.setXY([0,0]);
24896         this.el.show();
24897         //this.el.dom.style.display='block';
24898         
24899         //this.el.appendTo(on_el);
24900         
24901         var p = this.getPosition();
24902         var box = this.el.getBox();
24903         
24904         if (autoPlace) {
24905             // fixme..
24906         }
24907         
24908         var align = Roo.bootstrap.Tooltip.alignment[placement];
24909         
24910         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24911         
24912         if(placement == 'top' || placement == 'bottom'){
24913             if(xy[0] < 0){
24914                 placement = 'right';
24915             }
24916             
24917             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24918                 placement = 'left';
24919             }
24920             
24921             var scroll = Roo.select('body', true).first().getScroll();
24922             
24923             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24924                 placement = 'top';
24925             }
24926             
24927         }
24928         
24929         align = Roo.bootstrap.Tooltip.alignment[placement];
24930         
24931         this.el.alignTo(this.bindEl, align[0],align[1]);
24932         //var arrow = this.el.select('.arrow',true).first();
24933         //arrow.set(align[2], 
24934         
24935         this.el.addClass(placement);
24936         
24937         this.el.addClass('in fade');
24938         
24939         this.hoverState = null;
24940         
24941         if (this.el.hasClass('fade')) {
24942             // fade it?
24943         }
24944         
24945     },
24946     hide : function()
24947     {
24948          
24949         if (!this.el) {
24950             return;
24951         }
24952         //this.el.setXY([0,0]);
24953         this.el.removeClass('in');
24954         //this.el.hide();
24955         
24956     }
24957     
24958 });
24959  
24960
24961  /*
24962  * - LGPL
24963  *
24964  * Location Picker
24965  * 
24966  */
24967
24968 /**
24969  * @class Roo.bootstrap.LocationPicker
24970  * @extends Roo.bootstrap.Component
24971  * Bootstrap LocationPicker class
24972  * @cfg {Number} latitude Position when init default 0
24973  * @cfg {Number} longitude Position when init default 0
24974  * @cfg {Number} zoom default 15
24975  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24976  * @cfg {Boolean} mapTypeControl default false
24977  * @cfg {Boolean} disableDoubleClickZoom default false
24978  * @cfg {Boolean} scrollwheel default true
24979  * @cfg {Boolean} streetViewControl default false
24980  * @cfg {Number} radius default 0
24981  * @cfg {String} locationName
24982  * @cfg {Boolean} draggable default true
24983  * @cfg {Boolean} enableAutocomplete default false
24984  * @cfg {Boolean} enableReverseGeocode default true
24985  * @cfg {String} markerTitle
24986  * 
24987  * @constructor
24988  * Create a new LocationPicker
24989  * @param {Object} config The config object
24990  */
24991
24992
24993 Roo.bootstrap.LocationPicker = function(config){
24994     
24995     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24996     
24997     this.addEvents({
24998         /**
24999          * @event initial
25000          * Fires when the picker initialized.
25001          * @param {Roo.bootstrap.LocationPicker} this
25002          * @param {Google Location} location
25003          */
25004         initial : true,
25005         /**
25006          * @event positionchanged
25007          * Fires when the picker position changed.
25008          * @param {Roo.bootstrap.LocationPicker} this
25009          * @param {Google Location} location
25010          */
25011         positionchanged : true,
25012         /**
25013          * @event resize
25014          * Fires when the map resize.
25015          * @param {Roo.bootstrap.LocationPicker} this
25016          */
25017         resize : true,
25018         /**
25019          * @event show
25020          * Fires when the map show.
25021          * @param {Roo.bootstrap.LocationPicker} this
25022          */
25023         show : true,
25024         /**
25025          * @event hide
25026          * Fires when the map hide.
25027          * @param {Roo.bootstrap.LocationPicker} this
25028          */
25029         hide : true,
25030         /**
25031          * @event mapClick
25032          * Fires when click the map.
25033          * @param {Roo.bootstrap.LocationPicker} this
25034          * @param {Map event} e
25035          */
25036         mapClick : true,
25037         /**
25038          * @event mapRightClick
25039          * Fires when right click the map.
25040          * @param {Roo.bootstrap.LocationPicker} this
25041          * @param {Map event} e
25042          */
25043         mapRightClick : true,
25044         /**
25045          * @event markerClick
25046          * Fires when click the marker.
25047          * @param {Roo.bootstrap.LocationPicker} this
25048          * @param {Map event} e
25049          */
25050         markerClick : true,
25051         /**
25052          * @event markerRightClick
25053          * Fires when right click the marker.
25054          * @param {Roo.bootstrap.LocationPicker} this
25055          * @param {Map event} e
25056          */
25057         markerRightClick : true,
25058         /**
25059          * @event OverlayViewDraw
25060          * Fires when OverlayView Draw
25061          * @param {Roo.bootstrap.LocationPicker} this
25062          */
25063         OverlayViewDraw : true,
25064         /**
25065          * @event OverlayViewOnAdd
25066          * Fires when OverlayView Draw
25067          * @param {Roo.bootstrap.LocationPicker} this
25068          */
25069         OverlayViewOnAdd : true,
25070         /**
25071          * @event OverlayViewOnRemove
25072          * Fires when OverlayView Draw
25073          * @param {Roo.bootstrap.LocationPicker} this
25074          */
25075         OverlayViewOnRemove : true,
25076         /**
25077          * @event OverlayViewShow
25078          * Fires when OverlayView Draw
25079          * @param {Roo.bootstrap.LocationPicker} this
25080          * @param {Pixel} cpx
25081          */
25082         OverlayViewShow : true,
25083         /**
25084          * @event OverlayViewHide
25085          * Fires when OverlayView Draw
25086          * @param {Roo.bootstrap.LocationPicker} this
25087          */
25088         OverlayViewHide : true,
25089         /**
25090          * @event loadexception
25091          * Fires when load google lib failed.
25092          * @param {Roo.bootstrap.LocationPicker} this
25093          */
25094         loadexception : true
25095     });
25096         
25097 };
25098
25099 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25100     
25101     gMapContext: false,
25102     
25103     latitude: 0,
25104     longitude: 0,
25105     zoom: 15,
25106     mapTypeId: false,
25107     mapTypeControl: false,
25108     disableDoubleClickZoom: false,
25109     scrollwheel: true,
25110     streetViewControl: false,
25111     radius: 0,
25112     locationName: '',
25113     draggable: true,
25114     enableAutocomplete: false,
25115     enableReverseGeocode: true,
25116     markerTitle: '',
25117     
25118     getAutoCreate: function()
25119     {
25120
25121         var cfg = {
25122             tag: 'div',
25123             cls: 'roo-location-picker'
25124         };
25125         
25126         return cfg
25127     },
25128     
25129     initEvents: function(ct, position)
25130     {       
25131         if(!this.el.getWidth() || this.isApplied()){
25132             return;
25133         }
25134         
25135         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25136         
25137         this.initial();
25138     },
25139     
25140     initial: function()
25141     {
25142         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25143             this.fireEvent('loadexception', this);
25144             return;
25145         }
25146         
25147         if(!this.mapTypeId){
25148             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25149         }
25150         
25151         this.gMapContext = this.GMapContext();
25152         
25153         this.initOverlayView();
25154         
25155         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25156         
25157         var _this = this;
25158                 
25159         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25160             _this.setPosition(_this.gMapContext.marker.position);
25161         });
25162         
25163         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25164             _this.fireEvent('mapClick', this, event);
25165             
25166         });
25167
25168         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25169             _this.fireEvent('mapRightClick', this, event);
25170             
25171         });
25172         
25173         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25174             _this.fireEvent('markerClick', this, event);
25175             
25176         });
25177
25178         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25179             _this.fireEvent('markerRightClick', this, event);
25180             
25181         });
25182         
25183         this.setPosition(this.gMapContext.location);
25184         
25185         this.fireEvent('initial', this, this.gMapContext.location);
25186     },
25187     
25188     initOverlayView: function()
25189     {
25190         var _this = this;
25191         
25192         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25193             
25194             draw: function()
25195             {
25196                 _this.fireEvent('OverlayViewDraw', _this);
25197             },
25198             
25199             onAdd: function()
25200             {
25201                 _this.fireEvent('OverlayViewOnAdd', _this);
25202             },
25203             
25204             onRemove: function()
25205             {
25206                 _this.fireEvent('OverlayViewOnRemove', _this);
25207             },
25208             
25209             show: function(cpx)
25210             {
25211                 _this.fireEvent('OverlayViewShow', _this, cpx);
25212             },
25213             
25214             hide: function()
25215             {
25216                 _this.fireEvent('OverlayViewHide', _this);
25217             }
25218             
25219         });
25220     },
25221     
25222     fromLatLngToContainerPixel: function(event)
25223     {
25224         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25225     },
25226     
25227     isApplied: function() 
25228     {
25229         return this.getGmapContext() == false ? false : true;
25230     },
25231     
25232     getGmapContext: function() 
25233     {
25234         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25235     },
25236     
25237     GMapContext: function() 
25238     {
25239         var position = new google.maps.LatLng(this.latitude, this.longitude);
25240         
25241         var _map = new google.maps.Map(this.el.dom, {
25242             center: position,
25243             zoom: this.zoom,
25244             mapTypeId: this.mapTypeId,
25245             mapTypeControl: this.mapTypeControl,
25246             disableDoubleClickZoom: this.disableDoubleClickZoom,
25247             scrollwheel: this.scrollwheel,
25248             streetViewControl: this.streetViewControl,
25249             locationName: this.locationName,
25250             draggable: this.draggable,
25251             enableAutocomplete: this.enableAutocomplete,
25252             enableReverseGeocode: this.enableReverseGeocode
25253         });
25254         
25255         var _marker = new google.maps.Marker({
25256             position: position,
25257             map: _map,
25258             title: this.markerTitle,
25259             draggable: this.draggable
25260         });
25261         
25262         return {
25263             map: _map,
25264             marker: _marker,
25265             circle: null,
25266             location: position,
25267             radius: this.radius,
25268             locationName: this.locationName,
25269             addressComponents: {
25270                 formatted_address: null,
25271                 addressLine1: null,
25272                 addressLine2: null,
25273                 streetName: null,
25274                 streetNumber: null,
25275                 city: null,
25276                 district: null,
25277                 state: null,
25278                 stateOrProvince: null
25279             },
25280             settings: this,
25281             domContainer: this.el.dom,
25282             geodecoder: new google.maps.Geocoder()
25283         };
25284     },
25285     
25286     drawCircle: function(center, radius, options) 
25287     {
25288         if (this.gMapContext.circle != null) {
25289             this.gMapContext.circle.setMap(null);
25290         }
25291         if (radius > 0) {
25292             radius *= 1;
25293             options = Roo.apply({}, options, {
25294                 strokeColor: "#0000FF",
25295                 strokeOpacity: .35,
25296                 strokeWeight: 2,
25297                 fillColor: "#0000FF",
25298                 fillOpacity: .2
25299             });
25300             
25301             options.map = this.gMapContext.map;
25302             options.radius = radius;
25303             options.center = center;
25304             this.gMapContext.circle = new google.maps.Circle(options);
25305             return this.gMapContext.circle;
25306         }
25307         
25308         return null;
25309     },
25310     
25311     setPosition: function(location) 
25312     {
25313         this.gMapContext.location = location;
25314         this.gMapContext.marker.setPosition(location);
25315         this.gMapContext.map.panTo(location);
25316         this.drawCircle(location, this.gMapContext.radius, {});
25317         
25318         var _this = this;
25319         
25320         if (this.gMapContext.settings.enableReverseGeocode) {
25321             this.gMapContext.geodecoder.geocode({
25322                 latLng: this.gMapContext.location
25323             }, function(results, status) {
25324                 
25325                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25326                     _this.gMapContext.locationName = results[0].formatted_address;
25327                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25328                     
25329                     _this.fireEvent('positionchanged', this, location);
25330                 }
25331             });
25332             
25333             return;
25334         }
25335         
25336         this.fireEvent('positionchanged', this, location);
25337     },
25338     
25339     resize: function()
25340     {
25341         google.maps.event.trigger(this.gMapContext.map, "resize");
25342         
25343         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25344         
25345         this.fireEvent('resize', this);
25346     },
25347     
25348     setPositionByLatLng: function(latitude, longitude)
25349     {
25350         this.setPosition(new google.maps.LatLng(latitude, longitude));
25351     },
25352     
25353     getCurrentPosition: function() 
25354     {
25355         return {
25356             latitude: this.gMapContext.location.lat(),
25357             longitude: this.gMapContext.location.lng()
25358         };
25359     },
25360     
25361     getAddressName: function() 
25362     {
25363         return this.gMapContext.locationName;
25364     },
25365     
25366     getAddressComponents: function() 
25367     {
25368         return this.gMapContext.addressComponents;
25369     },
25370     
25371     address_component_from_google_geocode: function(address_components) 
25372     {
25373         var result = {};
25374         
25375         for (var i = 0; i < address_components.length; i++) {
25376             var component = address_components[i];
25377             if (component.types.indexOf("postal_code") >= 0) {
25378                 result.postalCode = component.short_name;
25379             } else if (component.types.indexOf("street_number") >= 0) {
25380                 result.streetNumber = component.short_name;
25381             } else if (component.types.indexOf("route") >= 0) {
25382                 result.streetName = component.short_name;
25383             } else if (component.types.indexOf("neighborhood") >= 0) {
25384                 result.city = component.short_name;
25385             } else if (component.types.indexOf("locality") >= 0) {
25386                 result.city = component.short_name;
25387             } else if (component.types.indexOf("sublocality") >= 0) {
25388                 result.district = component.short_name;
25389             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25390                 result.stateOrProvince = component.short_name;
25391             } else if (component.types.indexOf("country") >= 0) {
25392                 result.country = component.short_name;
25393             }
25394         }
25395         
25396         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25397         result.addressLine2 = "";
25398         return result;
25399     },
25400     
25401     setZoomLevel: function(zoom)
25402     {
25403         this.gMapContext.map.setZoom(zoom);
25404     },
25405     
25406     show: function()
25407     {
25408         if(!this.el){
25409             return;
25410         }
25411         
25412         this.el.show();
25413         
25414         this.resize();
25415         
25416         this.fireEvent('show', this);
25417     },
25418     
25419     hide: function()
25420     {
25421         if(!this.el){
25422             return;
25423         }
25424         
25425         this.el.hide();
25426         
25427         this.fireEvent('hide', this);
25428     }
25429     
25430 });
25431
25432 Roo.apply(Roo.bootstrap.LocationPicker, {
25433     
25434     OverlayView : function(map, options)
25435     {
25436         options = options || {};
25437         
25438         this.setMap(map);
25439     }
25440     
25441     
25442 });/*
25443  * - LGPL
25444  *
25445  * Alert
25446  * 
25447  */
25448
25449 /**
25450  * @class Roo.bootstrap.Alert
25451  * @extends Roo.bootstrap.Component
25452  * Bootstrap Alert class
25453  * @cfg {String} title The title of alert
25454  * @cfg {String} html The content of alert
25455  * @cfg {String} weight (  success | info | warning | danger )
25456  * @cfg {String} faicon font-awesomeicon
25457  * 
25458  * @constructor
25459  * Create a new alert
25460  * @param {Object} config The config object
25461  */
25462
25463
25464 Roo.bootstrap.Alert = function(config){
25465     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25466     
25467 };
25468
25469 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25470     
25471     title: '',
25472     html: '',
25473     weight: false,
25474     faicon: false,
25475     
25476     getAutoCreate : function()
25477     {
25478         
25479         var cfg = {
25480             tag : 'div',
25481             cls : 'alert',
25482             cn : [
25483                 {
25484                     tag : 'i',
25485                     cls : 'roo-alert-icon'
25486                     
25487                 },
25488                 {
25489                     tag : 'b',
25490                     cls : 'roo-alert-title',
25491                     html : this.title
25492                 },
25493                 {
25494                     tag : 'span',
25495                     cls : 'roo-alert-text',
25496                     html : this.html
25497                 }
25498             ]
25499         };
25500         
25501         if(this.faicon){
25502             cfg.cn[0].cls += ' fa ' + this.faicon;
25503         }
25504         
25505         if(this.weight){
25506             cfg.cls += ' alert-' + this.weight;
25507         }
25508         
25509         return cfg;
25510     },
25511     
25512     initEvents: function() 
25513     {
25514         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25515     },
25516     
25517     setTitle : function(str)
25518     {
25519         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25520     },
25521     
25522     setText : function(str)
25523     {
25524         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25525     },
25526     
25527     setWeight : function(weight)
25528     {
25529         if(this.weight){
25530             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25531         }
25532         
25533         this.weight = weight;
25534         
25535         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25536     },
25537     
25538     setIcon : function(icon)
25539     {
25540         if(this.faicon){
25541             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25542         }
25543         
25544         this.faicon = icon;
25545         
25546         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25547     },
25548     
25549     hide: function() 
25550     {
25551         this.el.hide();   
25552     },
25553     
25554     show: function() 
25555     {  
25556         this.el.show();   
25557     }
25558     
25559 });
25560
25561  
25562 /*
25563 * Licence: LGPL
25564 */
25565
25566 /**
25567  * @class Roo.bootstrap.UploadCropbox
25568  * @extends Roo.bootstrap.Component
25569  * Bootstrap UploadCropbox class
25570  * @cfg {String} emptyText show when image has been loaded
25571  * @cfg {String} rotateNotify show when image too small to rotate
25572  * @cfg {Number} errorTimeout default 3000
25573  * @cfg {Number} minWidth default 300
25574  * @cfg {Number} minHeight default 300
25575  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25576  * @cfg {Boolean} isDocument (true|false) default false
25577  * @cfg {String} url action url
25578  * @cfg {String} paramName default 'imageUpload'
25579  * @cfg {String} method default POST
25580  * @cfg {Boolean} loadMask (true|false) default true
25581  * @cfg {Boolean} loadingText default 'Loading...'
25582  * 
25583  * @constructor
25584  * Create a new UploadCropbox
25585  * @param {Object} config The config object
25586  */
25587
25588 Roo.bootstrap.UploadCropbox = function(config){
25589     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25590     
25591     this.addEvents({
25592         /**
25593          * @event beforeselectfile
25594          * Fire before select file
25595          * @param {Roo.bootstrap.UploadCropbox} this
25596          */
25597         "beforeselectfile" : true,
25598         /**
25599          * @event initial
25600          * Fire after initEvent
25601          * @param {Roo.bootstrap.UploadCropbox} this
25602          */
25603         "initial" : true,
25604         /**
25605          * @event crop
25606          * Fire after initEvent
25607          * @param {Roo.bootstrap.UploadCropbox} this
25608          * @param {String} data
25609          */
25610         "crop" : true,
25611         /**
25612          * @event prepare
25613          * Fire when preparing the file data
25614          * @param {Roo.bootstrap.UploadCropbox} this
25615          * @param {Object} file
25616          */
25617         "prepare" : true,
25618         /**
25619          * @event exception
25620          * Fire when get exception
25621          * @param {Roo.bootstrap.UploadCropbox} this
25622          * @param {XMLHttpRequest} xhr
25623          */
25624         "exception" : true,
25625         /**
25626          * @event beforeloadcanvas
25627          * Fire before load the canvas
25628          * @param {Roo.bootstrap.UploadCropbox} this
25629          * @param {String} src
25630          */
25631         "beforeloadcanvas" : true,
25632         /**
25633          * @event trash
25634          * Fire when trash image
25635          * @param {Roo.bootstrap.UploadCropbox} this
25636          */
25637         "trash" : true,
25638         /**
25639          * @event download
25640          * Fire when download the image
25641          * @param {Roo.bootstrap.UploadCropbox} this
25642          */
25643         "download" : true,
25644         /**
25645          * @event footerbuttonclick
25646          * Fire when footerbuttonclick
25647          * @param {Roo.bootstrap.UploadCropbox} this
25648          * @param {String} type
25649          */
25650         "footerbuttonclick" : true,
25651         /**
25652          * @event resize
25653          * Fire when resize
25654          * @param {Roo.bootstrap.UploadCropbox} this
25655          */
25656         "resize" : true,
25657         /**
25658          * @event rotate
25659          * Fire when rotate the image
25660          * @param {Roo.bootstrap.UploadCropbox} this
25661          * @param {String} pos
25662          */
25663         "rotate" : true,
25664         /**
25665          * @event inspect
25666          * Fire when inspect the file
25667          * @param {Roo.bootstrap.UploadCropbox} this
25668          * @param {Object} file
25669          */
25670         "inspect" : true,
25671         /**
25672          * @event upload
25673          * Fire when xhr upload the file
25674          * @param {Roo.bootstrap.UploadCropbox} this
25675          * @param {Object} data
25676          */
25677         "upload" : true,
25678         /**
25679          * @event arrange
25680          * Fire when arrange the file data
25681          * @param {Roo.bootstrap.UploadCropbox} this
25682          * @param {Object} formData
25683          */
25684         "arrange" : true
25685     });
25686     
25687     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25688 };
25689
25690 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25691     
25692     emptyText : 'Click to upload image',
25693     rotateNotify : 'Image is too small to rotate',
25694     errorTimeout : 3000,
25695     scale : 0,
25696     baseScale : 1,
25697     rotate : 0,
25698     dragable : false,
25699     pinching : false,
25700     mouseX : 0,
25701     mouseY : 0,
25702     cropData : false,
25703     minWidth : 300,
25704     minHeight : 300,
25705     file : false,
25706     exif : {},
25707     baseRotate : 1,
25708     cropType : 'image/jpeg',
25709     buttons : false,
25710     canvasLoaded : false,
25711     isDocument : false,
25712     method : 'POST',
25713     paramName : 'imageUpload',
25714     loadMask : true,
25715     loadingText : 'Loading...',
25716     maskEl : false,
25717     
25718     getAutoCreate : function()
25719     {
25720         var cfg = {
25721             tag : 'div',
25722             cls : 'roo-upload-cropbox',
25723             cn : [
25724                 {
25725                     tag : 'input',
25726                     cls : 'roo-upload-cropbox-selector',
25727                     type : 'file'
25728                 },
25729                 {
25730                     tag : 'div',
25731                     cls : 'roo-upload-cropbox-body',
25732                     style : 'cursor:pointer',
25733                     cn : [
25734                         {
25735                             tag : 'div',
25736                             cls : 'roo-upload-cropbox-preview'
25737                         },
25738                         {
25739                             tag : 'div',
25740                             cls : 'roo-upload-cropbox-thumb'
25741                         },
25742                         {
25743                             tag : 'div',
25744                             cls : 'roo-upload-cropbox-empty-notify',
25745                             html : this.emptyText
25746                         },
25747                         {
25748                             tag : 'div',
25749                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25750                             html : this.rotateNotify
25751                         }
25752                     ]
25753                 },
25754                 {
25755                     tag : 'div',
25756                     cls : 'roo-upload-cropbox-footer',
25757                     cn : {
25758                         tag : 'div',
25759                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25760                         cn : []
25761                     }
25762                 }
25763             ]
25764         };
25765         
25766         return cfg;
25767     },
25768     
25769     onRender : function(ct, position)
25770     {
25771         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25772         
25773         if (this.buttons.length) {
25774             
25775             Roo.each(this.buttons, function(bb) {
25776                 
25777                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25778                 
25779                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25780                 
25781             }, this);
25782         }
25783         
25784         if(this.loadMask){
25785             this.maskEl = this.el;
25786         }
25787     },
25788     
25789     initEvents : function()
25790     {
25791         this.urlAPI = (window.createObjectURL && window) || 
25792                                 (window.URL && URL.revokeObjectURL && URL) || 
25793                                 (window.webkitURL && webkitURL);
25794                         
25795         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25796         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25797         
25798         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25799         this.selectorEl.hide();
25800         
25801         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25802         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25803         
25804         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25805         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25806         this.thumbEl.hide();
25807         
25808         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25809         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25810         
25811         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25812         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25813         this.errorEl.hide();
25814         
25815         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25816         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25817         this.footerEl.hide();
25818         
25819         this.setThumbBoxSize();
25820         
25821         this.bind();
25822         
25823         this.resize();
25824         
25825         this.fireEvent('initial', this);
25826     },
25827
25828     bind : function()
25829     {
25830         var _this = this;
25831         
25832         window.addEventListener("resize", function() { _this.resize(); } );
25833         
25834         this.bodyEl.on('click', this.beforeSelectFile, this);
25835         
25836         if(Roo.isTouch){
25837             this.bodyEl.on('touchstart', this.onTouchStart, this);
25838             this.bodyEl.on('touchmove', this.onTouchMove, this);
25839             this.bodyEl.on('touchend', this.onTouchEnd, this);
25840         }
25841         
25842         if(!Roo.isTouch){
25843             this.bodyEl.on('mousedown', this.onMouseDown, this);
25844             this.bodyEl.on('mousemove', this.onMouseMove, this);
25845             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25846             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25847             Roo.get(document).on('mouseup', this.onMouseUp, this);
25848         }
25849         
25850         this.selectorEl.on('change', this.onFileSelected, this);
25851     },
25852     
25853     reset : function()
25854     {    
25855         this.scale = 0;
25856         this.baseScale = 1;
25857         this.rotate = 0;
25858         this.baseRotate = 1;
25859         this.dragable = false;
25860         this.pinching = false;
25861         this.mouseX = 0;
25862         this.mouseY = 0;
25863         this.cropData = false;
25864         this.notifyEl.dom.innerHTML = this.emptyText;
25865         
25866         this.selectorEl.dom.value = '';
25867         
25868     },
25869     
25870     resize : function()
25871     {
25872         if(this.fireEvent('resize', this) != false){
25873             this.setThumbBoxPosition();
25874             this.setCanvasPosition();
25875         }
25876     },
25877     
25878     onFooterButtonClick : function(e, el, o, type)
25879     {
25880         switch (type) {
25881             case 'rotate-left' :
25882                 this.onRotateLeft(e);
25883                 break;
25884             case 'rotate-right' :
25885                 this.onRotateRight(e);
25886                 break;
25887             case 'picture' :
25888                 this.beforeSelectFile(e);
25889                 break;
25890             case 'trash' :
25891                 this.trash(e);
25892                 break;
25893             case 'crop' :
25894                 this.crop(e);
25895                 break;
25896             case 'download' :
25897                 this.download(e);
25898                 break;
25899             default :
25900                 break;
25901         }
25902         
25903         this.fireEvent('footerbuttonclick', this, type);
25904     },
25905     
25906     beforeSelectFile : function(e)
25907     {
25908         e.preventDefault();
25909         
25910         if(this.fireEvent('beforeselectfile', this) != false){
25911             this.selectorEl.dom.click();
25912         }
25913     },
25914     
25915     onFileSelected : function(e)
25916     {
25917         e.preventDefault();
25918         
25919         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25920             return;
25921         }
25922         
25923         var file = this.selectorEl.dom.files[0];
25924         
25925         if(this.fireEvent('inspect', this, file) != false){
25926             this.prepare(file);
25927         }
25928         
25929     },
25930     
25931     trash : function(e)
25932     {
25933         this.fireEvent('trash', this);
25934     },
25935     
25936     download : function(e)
25937     {
25938         this.fireEvent('download', this);
25939     },
25940     
25941     loadCanvas : function(src)
25942     {   
25943         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25944             
25945             this.reset();
25946             
25947             this.imageEl = document.createElement('img');
25948             
25949             var _this = this;
25950             
25951             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25952             
25953             this.imageEl.src = src;
25954         }
25955     },
25956     
25957     onLoadCanvas : function()
25958     {   
25959         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25960         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25961         
25962         this.bodyEl.un('click', this.beforeSelectFile, this);
25963         
25964         this.notifyEl.hide();
25965         this.thumbEl.show();
25966         this.footerEl.show();
25967         
25968         this.baseRotateLevel();
25969         
25970         if(this.isDocument){
25971             this.setThumbBoxSize();
25972         }
25973         
25974         this.setThumbBoxPosition();
25975         
25976         this.baseScaleLevel();
25977         
25978         this.draw();
25979         
25980         this.resize();
25981         
25982         this.canvasLoaded = true;
25983         
25984         if(this.loadMask){
25985             this.maskEl.unmask();
25986         }
25987         
25988     },
25989     
25990     setCanvasPosition : function()
25991     {   
25992         if(!this.canvasEl){
25993             return;
25994         }
25995         
25996         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25997         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25998         
25999         this.previewEl.setLeft(pw);
26000         this.previewEl.setTop(ph);
26001         
26002     },
26003     
26004     onMouseDown : function(e)
26005     {   
26006         e.stopEvent();
26007         
26008         this.dragable = true;
26009         this.pinching = false;
26010         
26011         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26012             this.dragable = false;
26013             return;
26014         }
26015         
26016         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26017         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26018         
26019     },
26020     
26021     onMouseMove : function(e)
26022     {   
26023         e.stopEvent();
26024         
26025         if(!this.canvasLoaded){
26026             return;
26027         }
26028         
26029         if (!this.dragable){
26030             return;
26031         }
26032         
26033         var minX = Math.ceil(this.thumbEl.getLeft(true));
26034         var minY = Math.ceil(this.thumbEl.getTop(true));
26035         
26036         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26037         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26038         
26039         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26040         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26041         
26042         x = x - this.mouseX;
26043         y = y - this.mouseY;
26044         
26045         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26046         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26047         
26048         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26049         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26050         
26051         this.previewEl.setLeft(bgX);
26052         this.previewEl.setTop(bgY);
26053         
26054         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26055         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26056     },
26057     
26058     onMouseUp : function(e)
26059     {   
26060         e.stopEvent();
26061         
26062         this.dragable = false;
26063     },
26064     
26065     onMouseWheel : function(e)
26066     {   
26067         e.stopEvent();
26068         
26069         this.startScale = this.scale;
26070         
26071         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26072         
26073         if(!this.zoomable()){
26074             this.scale = this.startScale;
26075             return;
26076         }
26077         
26078         this.draw();
26079         
26080         return;
26081     },
26082     
26083     zoomable : function()
26084     {
26085         var minScale = this.thumbEl.getWidth() / this.minWidth;
26086         
26087         if(this.minWidth < this.minHeight){
26088             minScale = this.thumbEl.getHeight() / this.minHeight;
26089         }
26090         
26091         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26092         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26093         
26094         if(
26095                 this.isDocument &&
26096                 (this.rotate == 0 || this.rotate == 180) && 
26097                 (
26098                     width > this.imageEl.OriginWidth || 
26099                     height > this.imageEl.OriginHeight ||
26100                     (width < this.minWidth && height < this.minHeight)
26101                 )
26102         ){
26103             return false;
26104         }
26105         
26106         if(
26107                 this.isDocument &&
26108                 (this.rotate == 90 || this.rotate == 270) && 
26109                 (
26110                     width > this.imageEl.OriginWidth || 
26111                     height > this.imageEl.OriginHeight ||
26112                     (width < this.minHeight && height < this.minWidth)
26113                 )
26114         ){
26115             return false;
26116         }
26117         
26118         if(
26119                 !this.isDocument &&
26120                 (this.rotate == 0 || this.rotate == 180) && 
26121                 (
26122                     width < this.minWidth || 
26123                     width > this.imageEl.OriginWidth || 
26124                     height < this.minHeight || 
26125                     height > this.imageEl.OriginHeight
26126                 )
26127         ){
26128             return false;
26129         }
26130         
26131         if(
26132                 !this.isDocument &&
26133                 (this.rotate == 90 || this.rotate == 270) && 
26134                 (
26135                     width < this.minHeight || 
26136                     width > this.imageEl.OriginWidth || 
26137                     height < this.minWidth || 
26138                     height > this.imageEl.OriginHeight
26139                 )
26140         ){
26141             return false;
26142         }
26143         
26144         return true;
26145         
26146     },
26147     
26148     onRotateLeft : function(e)
26149     {   
26150         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26151             
26152             var minScale = this.thumbEl.getWidth() / this.minWidth;
26153             
26154             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26155             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26156             
26157             this.startScale = this.scale;
26158             
26159             while (this.getScaleLevel() < minScale){
26160             
26161                 this.scale = this.scale + 1;
26162                 
26163                 if(!this.zoomable()){
26164                     break;
26165                 }
26166                 
26167                 if(
26168                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26169                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26170                 ){
26171                     continue;
26172                 }
26173                 
26174                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26175
26176                 this.draw();
26177                 
26178                 return;
26179             }
26180             
26181             this.scale = this.startScale;
26182             
26183             this.onRotateFail();
26184             
26185             return false;
26186         }
26187         
26188         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26189
26190         if(this.isDocument){
26191             this.setThumbBoxSize();
26192             this.setThumbBoxPosition();
26193             this.setCanvasPosition();
26194         }
26195         
26196         this.draw();
26197         
26198         this.fireEvent('rotate', this, 'left');
26199         
26200     },
26201     
26202     onRotateRight : function(e)
26203     {
26204         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26205             
26206             var minScale = this.thumbEl.getWidth() / this.minWidth;
26207         
26208             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26209             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26210             
26211             this.startScale = this.scale;
26212             
26213             while (this.getScaleLevel() < minScale){
26214             
26215                 this.scale = this.scale + 1;
26216                 
26217                 if(!this.zoomable()){
26218                     break;
26219                 }
26220                 
26221                 if(
26222                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26223                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26224                 ){
26225                     continue;
26226                 }
26227                 
26228                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26229
26230                 this.draw();
26231                 
26232                 return;
26233             }
26234             
26235             this.scale = this.startScale;
26236             
26237             this.onRotateFail();
26238             
26239             return false;
26240         }
26241         
26242         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26243
26244         if(this.isDocument){
26245             this.setThumbBoxSize();
26246             this.setThumbBoxPosition();
26247             this.setCanvasPosition();
26248         }
26249         
26250         this.draw();
26251         
26252         this.fireEvent('rotate', this, 'right');
26253     },
26254     
26255     onRotateFail : function()
26256     {
26257         this.errorEl.show(true);
26258         
26259         var _this = this;
26260         
26261         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26262     },
26263     
26264     draw : function()
26265     {
26266         this.previewEl.dom.innerHTML = '';
26267         
26268         var canvasEl = document.createElement("canvas");
26269         
26270         var contextEl = canvasEl.getContext("2d");
26271         
26272         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26273         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26274         var center = this.imageEl.OriginWidth / 2;
26275         
26276         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26277             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26278             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26279             center = this.imageEl.OriginHeight / 2;
26280         }
26281         
26282         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26283         
26284         contextEl.translate(center, center);
26285         contextEl.rotate(this.rotate * Math.PI / 180);
26286
26287         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26288         
26289         this.canvasEl = document.createElement("canvas");
26290         
26291         this.contextEl = this.canvasEl.getContext("2d");
26292         
26293         switch (this.rotate) {
26294             case 0 :
26295                 
26296                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26297                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26298                 
26299                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26300                 
26301                 break;
26302             case 90 : 
26303                 
26304                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26305                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26306                 
26307                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26308                     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);
26309                     break;
26310                 }
26311                 
26312                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26313                 
26314                 break;
26315             case 180 :
26316                 
26317                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26318                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26319                 
26320                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26321                     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);
26322                     break;
26323                 }
26324                 
26325                 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);
26326                 
26327                 break;
26328             case 270 :
26329                 
26330                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26331                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26332         
26333                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26334                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26335                     break;
26336                 }
26337                 
26338                 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);
26339                 
26340                 break;
26341             default : 
26342                 break;
26343         }
26344         
26345         this.previewEl.appendChild(this.canvasEl);
26346         
26347         this.setCanvasPosition();
26348     },
26349     
26350     crop : function()
26351     {
26352         if(!this.canvasLoaded){
26353             return;
26354         }
26355         
26356         var imageCanvas = document.createElement("canvas");
26357         
26358         var imageContext = imageCanvas.getContext("2d");
26359         
26360         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26361         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26362         
26363         var center = imageCanvas.width / 2;
26364         
26365         imageContext.translate(center, center);
26366         
26367         imageContext.rotate(this.rotate * Math.PI / 180);
26368         
26369         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26370         
26371         var canvas = document.createElement("canvas");
26372         
26373         var context = canvas.getContext("2d");
26374                 
26375         canvas.width = this.minWidth;
26376         canvas.height = this.minHeight;
26377
26378         switch (this.rotate) {
26379             case 0 :
26380                 
26381                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26382                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26383                 
26384                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26385                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26386                 
26387                 var targetWidth = this.minWidth - 2 * x;
26388                 var targetHeight = this.minHeight - 2 * y;
26389                 
26390                 var scale = 1;
26391                 
26392                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26393                     scale = targetWidth / width;
26394                 }
26395                 
26396                 if(x > 0 && y == 0){
26397                     scale = targetHeight / height;
26398                 }
26399                 
26400                 if(x > 0 && y > 0){
26401                     scale = targetWidth / width;
26402                     
26403                     if(width < height){
26404                         scale = targetHeight / height;
26405                     }
26406                 }
26407                 
26408                 context.scale(scale, scale);
26409                 
26410                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26411                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26412
26413                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26414                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26415
26416                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26417                 
26418                 break;
26419             case 90 : 
26420                 
26421                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26422                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26423                 
26424                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26425                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26426                 
26427                 var targetWidth = this.minWidth - 2 * x;
26428                 var targetHeight = this.minHeight - 2 * y;
26429                 
26430                 var scale = 1;
26431                 
26432                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26433                     scale = targetWidth / width;
26434                 }
26435                 
26436                 if(x > 0 && y == 0){
26437                     scale = targetHeight / height;
26438                 }
26439                 
26440                 if(x > 0 && y > 0){
26441                     scale = targetWidth / width;
26442                     
26443                     if(width < height){
26444                         scale = targetHeight / height;
26445                     }
26446                 }
26447                 
26448                 context.scale(scale, scale);
26449                 
26450                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26451                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26452
26453                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26454                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26455                 
26456                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26457                 
26458                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26459                 
26460                 break;
26461             case 180 :
26462                 
26463                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26464                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26465                 
26466                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26467                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26468                 
26469                 var targetWidth = this.minWidth - 2 * x;
26470                 var targetHeight = this.minHeight - 2 * y;
26471                 
26472                 var scale = 1;
26473                 
26474                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26475                     scale = targetWidth / width;
26476                 }
26477                 
26478                 if(x > 0 && y == 0){
26479                     scale = targetHeight / height;
26480                 }
26481                 
26482                 if(x > 0 && y > 0){
26483                     scale = targetWidth / width;
26484                     
26485                     if(width < height){
26486                         scale = targetHeight / height;
26487                     }
26488                 }
26489                 
26490                 context.scale(scale, scale);
26491                 
26492                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26493                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26494
26495                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26496                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26497
26498                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26499                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26500                 
26501                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26502                 
26503                 break;
26504             case 270 :
26505                 
26506                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26507                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26508                 
26509                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26510                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26511                 
26512                 var targetWidth = this.minWidth - 2 * x;
26513                 var targetHeight = this.minHeight - 2 * y;
26514                 
26515                 var scale = 1;
26516                 
26517                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26518                     scale = targetWidth / width;
26519                 }
26520                 
26521                 if(x > 0 && y == 0){
26522                     scale = targetHeight / height;
26523                 }
26524                 
26525                 if(x > 0 && y > 0){
26526                     scale = targetWidth / width;
26527                     
26528                     if(width < height){
26529                         scale = targetHeight / height;
26530                     }
26531                 }
26532                 
26533                 context.scale(scale, scale);
26534                 
26535                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26536                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26537
26538                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26539                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26540                 
26541                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26542                 
26543                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26544                 
26545                 break;
26546             default : 
26547                 break;
26548         }
26549         
26550         this.cropData = canvas.toDataURL(this.cropType);
26551         
26552         if(this.fireEvent('crop', this, this.cropData) !== false){
26553             this.process(this.file, this.cropData);
26554         }
26555         
26556         return;
26557         
26558     },
26559     
26560     setThumbBoxSize : function()
26561     {
26562         var width, height;
26563         
26564         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26565             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26566             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26567             
26568             this.minWidth = width;
26569             this.minHeight = height;
26570             
26571             if(this.rotate == 90 || this.rotate == 270){
26572                 this.minWidth = height;
26573                 this.minHeight = width;
26574             }
26575         }
26576         
26577         height = 300;
26578         width = Math.ceil(this.minWidth * height / this.minHeight);
26579         
26580         if(this.minWidth > this.minHeight){
26581             width = 300;
26582             height = Math.ceil(this.minHeight * width / this.minWidth);
26583         }
26584         
26585         this.thumbEl.setStyle({
26586             width : width + 'px',
26587             height : height + 'px'
26588         });
26589
26590         return;
26591             
26592     },
26593     
26594     setThumbBoxPosition : function()
26595     {
26596         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26597         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26598         
26599         this.thumbEl.setLeft(x);
26600         this.thumbEl.setTop(y);
26601         
26602     },
26603     
26604     baseRotateLevel : function()
26605     {
26606         this.baseRotate = 1;
26607         
26608         if(
26609                 typeof(this.exif) != 'undefined' &&
26610                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26611                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26612         ){
26613             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26614         }
26615         
26616         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26617         
26618     },
26619     
26620     baseScaleLevel : function()
26621     {
26622         var width, height;
26623         
26624         if(this.isDocument){
26625             
26626             if(this.baseRotate == 6 || this.baseRotate == 8){
26627             
26628                 height = this.thumbEl.getHeight();
26629                 this.baseScale = height / this.imageEl.OriginWidth;
26630
26631                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26632                     width = this.thumbEl.getWidth();
26633                     this.baseScale = width / this.imageEl.OriginHeight;
26634                 }
26635
26636                 return;
26637             }
26638
26639             height = this.thumbEl.getHeight();
26640             this.baseScale = height / this.imageEl.OriginHeight;
26641
26642             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26643                 width = this.thumbEl.getWidth();
26644                 this.baseScale = width / this.imageEl.OriginWidth;
26645             }
26646
26647             return;
26648         }
26649         
26650         if(this.baseRotate == 6 || this.baseRotate == 8){
26651             
26652             width = this.thumbEl.getHeight();
26653             this.baseScale = width / this.imageEl.OriginHeight;
26654             
26655             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26656                 height = this.thumbEl.getWidth();
26657                 this.baseScale = height / this.imageEl.OriginHeight;
26658             }
26659             
26660             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26661                 height = this.thumbEl.getWidth();
26662                 this.baseScale = height / this.imageEl.OriginHeight;
26663                 
26664                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26665                     width = this.thumbEl.getHeight();
26666                     this.baseScale = width / this.imageEl.OriginWidth;
26667                 }
26668             }
26669             
26670             return;
26671         }
26672         
26673         width = this.thumbEl.getWidth();
26674         this.baseScale = width / this.imageEl.OriginWidth;
26675         
26676         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26677             height = this.thumbEl.getHeight();
26678             this.baseScale = height / this.imageEl.OriginHeight;
26679         }
26680         
26681         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26682             
26683             height = this.thumbEl.getHeight();
26684             this.baseScale = height / this.imageEl.OriginHeight;
26685             
26686             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26687                 width = this.thumbEl.getWidth();
26688                 this.baseScale = width / this.imageEl.OriginWidth;
26689             }
26690             
26691         }
26692         
26693         return;
26694     },
26695     
26696     getScaleLevel : function()
26697     {
26698         return this.baseScale * Math.pow(1.1, this.scale);
26699     },
26700     
26701     onTouchStart : function(e)
26702     {
26703         if(!this.canvasLoaded){
26704             this.beforeSelectFile(e);
26705             return;
26706         }
26707         
26708         var touches = e.browserEvent.touches;
26709         
26710         if(!touches){
26711             return;
26712         }
26713         
26714         if(touches.length == 1){
26715             this.onMouseDown(e);
26716             return;
26717         }
26718         
26719         if(touches.length != 2){
26720             return;
26721         }
26722         
26723         var coords = [];
26724         
26725         for(var i = 0, finger; finger = touches[i]; i++){
26726             coords.push(finger.pageX, finger.pageY);
26727         }
26728         
26729         var x = Math.pow(coords[0] - coords[2], 2);
26730         var y = Math.pow(coords[1] - coords[3], 2);
26731         
26732         this.startDistance = Math.sqrt(x + y);
26733         
26734         this.startScale = this.scale;
26735         
26736         this.pinching = true;
26737         this.dragable = false;
26738         
26739     },
26740     
26741     onTouchMove : function(e)
26742     {
26743         if(!this.pinching && !this.dragable){
26744             return;
26745         }
26746         
26747         var touches = e.browserEvent.touches;
26748         
26749         if(!touches){
26750             return;
26751         }
26752         
26753         if(this.dragable){
26754             this.onMouseMove(e);
26755             return;
26756         }
26757         
26758         var coords = [];
26759         
26760         for(var i = 0, finger; finger = touches[i]; i++){
26761             coords.push(finger.pageX, finger.pageY);
26762         }
26763         
26764         var x = Math.pow(coords[0] - coords[2], 2);
26765         var y = Math.pow(coords[1] - coords[3], 2);
26766         
26767         this.endDistance = Math.sqrt(x + y);
26768         
26769         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26770         
26771         if(!this.zoomable()){
26772             this.scale = this.startScale;
26773             return;
26774         }
26775         
26776         this.draw();
26777         
26778     },
26779     
26780     onTouchEnd : function(e)
26781     {
26782         this.pinching = false;
26783         this.dragable = false;
26784         
26785     },
26786     
26787     process : function(file, crop)
26788     {
26789         if(this.loadMask){
26790             this.maskEl.mask(this.loadingText);
26791         }
26792         
26793         this.xhr = new XMLHttpRequest();
26794         
26795         file.xhr = this.xhr;
26796
26797         this.xhr.open(this.method, this.url, true);
26798         
26799         var headers = {
26800             "Accept": "application/json",
26801             "Cache-Control": "no-cache",
26802             "X-Requested-With": "XMLHttpRequest"
26803         };
26804         
26805         for (var headerName in headers) {
26806             var headerValue = headers[headerName];
26807             if (headerValue) {
26808                 this.xhr.setRequestHeader(headerName, headerValue);
26809             }
26810         }
26811         
26812         var _this = this;
26813         
26814         this.xhr.onload = function()
26815         {
26816             _this.xhrOnLoad(_this.xhr);
26817         }
26818         
26819         this.xhr.onerror = function()
26820         {
26821             _this.xhrOnError(_this.xhr);
26822         }
26823         
26824         var formData = new FormData();
26825
26826         formData.append('returnHTML', 'NO');
26827         
26828         if(crop){
26829             formData.append('crop', crop);
26830         }
26831         
26832         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26833             formData.append(this.paramName, file, file.name);
26834         }
26835         
26836         if(typeof(file.filename) != 'undefined'){
26837             formData.append('filename', file.filename);
26838         }
26839         
26840         if(typeof(file.mimetype) != 'undefined'){
26841             formData.append('mimetype', file.mimetype);
26842         }
26843         
26844         if(this.fireEvent('arrange', this, formData) != false){
26845             this.xhr.send(formData);
26846         };
26847     },
26848     
26849     xhrOnLoad : function(xhr)
26850     {
26851         if(this.loadMask){
26852             this.maskEl.unmask();
26853         }
26854         
26855         if (xhr.readyState !== 4) {
26856             this.fireEvent('exception', this, xhr);
26857             return;
26858         }
26859
26860         var response = Roo.decode(xhr.responseText);
26861         
26862         if(!response.success){
26863             this.fireEvent('exception', this, xhr);
26864             return;
26865         }
26866         
26867         var response = Roo.decode(xhr.responseText);
26868         
26869         this.fireEvent('upload', this, response);
26870         
26871     },
26872     
26873     xhrOnError : function()
26874     {
26875         if(this.loadMask){
26876             this.maskEl.unmask();
26877         }
26878         
26879         Roo.log('xhr on error');
26880         
26881         var response = Roo.decode(xhr.responseText);
26882           
26883         Roo.log(response);
26884         
26885     },
26886     
26887     prepare : function(file)
26888     {   
26889         if(this.loadMask){
26890             this.maskEl.mask(this.loadingText);
26891         }
26892         
26893         this.file = false;
26894         this.exif = {};
26895         
26896         if(typeof(file) === 'string'){
26897             this.loadCanvas(file);
26898             return;
26899         }
26900         
26901         if(!file || !this.urlAPI){
26902             return;
26903         }
26904         
26905         this.file = file;
26906         this.cropType = file.type;
26907         
26908         var _this = this;
26909         
26910         if(this.fireEvent('prepare', this, this.file) != false){
26911             
26912             var reader = new FileReader();
26913             
26914             reader.onload = function (e) {
26915                 if (e.target.error) {
26916                     Roo.log(e.target.error);
26917                     return;
26918                 }
26919                 
26920                 var buffer = e.target.result,
26921                     dataView = new DataView(buffer),
26922                     offset = 2,
26923                     maxOffset = dataView.byteLength - 4,
26924                     markerBytes,
26925                     markerLength;
26926                 
26927                 if (dataView.getUint16(0) === 0xffd8) {
26928                     while (offset < maxOffset) {
26929                         markerBytes = dataView.getUint16(offset);
26930                         
26931                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26932                             markerLength = dataView.getUint16(offset + 2) + 2;
26933                             if (offset + markerLength > dataView.byteLength) {
26934                                 Roo.log('Invalid meta data: Invalid segment size.');
26935                                 break;
26936                             }
26937                             
26938                             if(markerBytes == 0xffe1){
26939                                 _this.parseExifData(
26940                                     dataView,
26941                                     offset,
26942                                     markerLength
26943                                 );
26944                             }
26945                             
26946                             offset += markerLength;
26947                             
26948                             continue;
26949                         }
26950                         
26951                         break;
26952                     }
26953                     
26954                 }
26955                 
26956                 var url = _this.urlAPI.createObjectURL(_this.file);
26957                 
26958                 _this.loadCanvas(url);
26959                 
26960                 return;
26961             }
26962             
26963             reader.readAsArrayBuffer(this.file);
26964             
26965         }
26966         
26967     },
26968     
26969     parseExifData : function(dataView, offset, length)
26970     {
26971         var tiffOffset = offset + 10,
26972             littleEndian,
26973             dirOffset;
26974     
26975         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26976             // No Exif data, might be XMP data instead
26977             return;
26978         }
26979         
26980         // Check for the ASCII code for "Exif" (0x45786966):
26981         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26982             // No Exif data, might be XMP data instead
26983             return;
26984         }
26985         if (tiffOffset + 8 > dataView.byteLength) {
26986             Roo.log('Invalid Exif data: Invalid segment size.');
26987             return;
26988         }
26989         // Check for the two null bytes:
26990         if (dataView.getUint16(offset + 8) !== 0x0000) {
26991             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26992             return;
26993         }
26994         // Check the byte alignment:
26995         switch (dataView.getUint16(tiffOffset)) {
26996         case 0x4949:
26997             littleEndian = true;
26998             break;
26999         case 0x4D4D:
27000             littleEndian = false;
27001             break;
27002         default:
27003             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27004             return;
27005         }
27006         // Check for the TIFF tag marker (0x002A):
27007         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27008             Roo.log('Invalid Exif data: Missing TIFF marker.');
27009             return;
27010         }
27011         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27012         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27013         
27014         this.parseExifTags(
27015             dataView,
27016             tiffOffset,
27017             tiffOffset + dirOffset,
27018             littleEndian
27019         );
27020     },
27021     
27022     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27023     {
27024         var tagsNumber,
27025             dirEndOffset,
27026             i;
27027         if (dirOffset + 6 > dataView.byteLength) {
27028             Roo.log('Invalid Exif data: Invalid directory offset.');
27029             return;
27030         }
27031         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27032         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27033         if (dirEndOffset + 4 > dataView.byteLength) {
27034             Roo.log('Invalid Exif data: Invalid directory size.');
27035             return;
27036         }
27037         for (i = 0; i < tagsNumber; i += 1) {
27038             this.parseExifTag(
27039                 dataView,
27040                 tiffOffset,
27041                 dirOffset + 2 + 12 * i, // tag offset
27042                 littleEndian
27043             );
27044         }
27045         // Return the offset to the next directory:
27046         return dataView.getUint32(dirEndOffset, littleEndian);
27047     },
27048     
27049     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27050     {
27051         var tag = dataView.getUint16(offset, littleEndian);
27052         
27053         this.exif[tag] = this.getExifValue(
27054             dataView,
27055             tiffOffset,
27056             offset,
27057             dataView.getUint16(offset + 2, littleEndian), // tag type
27058             dataView.getUint32(offset + 4, littleEndian), // tag length
27059             littleEndian
27060         );
27061     },
27062     
27063     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27064     {
27065         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27066             tagSize,
27067             dataOffset,
27068             values,
27069             i,
27070             str,
27071             c;
27072     
27073         if (!tagType) {
27074             Roo.log('Invalid Exif data: Invalid tag type.');
27075             return;
27076         }
27077         
27078         tagSize = tagType.size * length;
27079         // Determine if the value is contained in the dataOffset bytes,
27080         // or if the value at the dataOffset is a pointer to the actual data:
27081         dataOffset = tagSize > 4 ?
27082                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27083         if (dataOffset + tagSize > dataView.byteLength) {
27084             Roo.log('Invalid Exif data: Invalid data offset.');
27085             return;
27086         }
27087         if (length === 1) {
27088             return tagType.getValue(dataView, dataOffset, littleEndian);
27089         }
27090         values = [];
27091         for (i = 0; i < length; i += 1) {
27092             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27093         }
27094         
27095         if (tagType.ascii) {
27096             str = '';
27097             // Concatenate the chars:
27098             for (i = 0; i < values.length; i += 1) {
27099                 c = values[i];
27100                 // Ignore the terminating NULL byte(s):
27101                 if (c === '\u0000') {
27102                     break;
27103                 }
27104                 str += c;
27105             }
27106             return str;
27107         }
27108         return values;
27109     }
27110     
27111 });
27112
27113 Roo.apply(Roo.bootstrap.UploadCropbox, {
27114     tags : {
27115         'Orientation': 0x0112
27116     },
27117     
27118     Orientation: {
27119             1: 0, //'top-left',
27120 //            2: 'top-right',
27121             3: 180, //'bottom-right',
27122 //            4: 'bottom-left',
27123 //            5: 'left-top',
27124             6: 90, //'right-top',
27125 //            7: 'right-bottom',
27126             8: 270 //'left-bottom'
27127     },
27128     
27129     exifTagTypes : {
27130         // byte, 8-bit unsigned int:
27131         1: {
27132             getValue: function (dataView, dataOffset) {
27133                 return dataView.getUint8(dataOffset);
27134             },
27135             size: 1
27136         },
27137         // ascii, 8-bit byte:
27138         2: {
27139             getValue: function (dataView, dataOffset) {
27140                 return String.fromCharCode(dataView.getUint8(dataOffset));
27141             },
27142             size: 1,
27143             ascii: true
27144         },
27145         // short, 16 bit int:
27146         3: {
27147             getValue: function (dataView, dataOffset, littleEndian) {
27148                 return dataView.getUint16(dataOffset, littleEndian);
27149             },
27150             size: 2
27151         },
27152         // long, 32 bit int:
27153         4: {
27154             getValue: function (dataView, dataOffset, littleEndian) {
27155                 return dataView.getUint32(dataOffset, littleEndian);
27156             },
27157             size: 4
27158         },
27159         // rational = two long values, first is numerator, second is denominator:
27160         5: {
27161             getValue: function (dataView, dataOffset, littleEndian) {
27162                 return dataView.getUint32(dataOffset, littleEndian) /
27163                     dataView.getUint32(dataOffset + 4, littleEndian);
27164             },
27165             size: 8
27166         },
27167         // slong, 32 bit signed int:
27168         9: {
27169             getValue: function (dataView, dataOffset, littleEndian) {
27170                 return dataView.getInt32(dataOffset, littleEndian);
27171             },
27172             size: 4
27173         },
27174         // srational, two slongs, first is numerator, second is denominator:
27175         10: {
27176             getValue: function (dataView, dataOffset, littleEndian) {
27177                 return dataView.getInt32(dataOffset, littleEndian) /
27178                     dataView.getInt32(dataOffset + 4, littleEndian);
27179             },
27180             size: 8
27181         }
27182     },
27183     
27184     footer : {
27185         STANDARD : [
27186             {
27187                 tag : 'div',
27188                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27189                 action : 'rotate-left',
27190                 cn : [
27191                     {
27192                         tag : 'button',
27193                         cls : 'btn btn-default',
27194                         html : '<i class="fa fa-undo"></i>'
27195                     }
27196                 ]
27197             },
27198             {
27199                 tag : 'div',
27200                 cls : 'btn-group roo-upload-cropbox-picture',
27201                 action : 'picture',
27202                 cn : [
27203                     {
27204                         tag : 'button',
27205                         cls : 'btn btn-default',
27206                         html : '<i class="fa fa-picture-o"></i>'
27207                     }
27208                 ]
27209             },
27210             {
27211                 tag : 'div',
27212                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27213                 action : 'rotate-right',
27214                 cn : [
27215                     {
27216                         tag : 'button',
27217                         cls : 'btn btn-default',
27218                         html : '<i class="fa fa-repeat"></i>'
27219                     }
27220                 ]
27221             }
27222         ],
27223         DOCUMENT : [
27224             {
27225                 tag : 'div',
27226                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27227                 action : 'rotate-left',
27228                 cn : [
27229                     {
27230                         tag : 'button',
27231                         cls : 'btn btn-default',
27232                         html : '<i class="fa fa-undo"></i>'
27233                     }
27234                 ]
27235             },
27236             {
27237                 tag : 'div',
27238                 cls : 'btn-group roo-upload-cropbox-download',
27239                 action : 'download',
27240                 cn : [
27241                     {
27242                         tag : 'button',
27243                         cls : 'btn btn-default',
27244                         html : '<i class="fa fa-download"></i>'
27245                     }
27246                 ]
27247             },
27248             {
27249                 tag : 'div',
27250                 cls : 'btn-group roo-upload-cropbox-crop',
27251                 action : 'crop',
27252                 cn : [
27253                     {
27254                         tag : 'button',
27255                         cls : 'btn btn-default',
27256                         html : '<i class="fa fa-crop"></i>'
27257                     }
27258                 ]
27259             },
27260             {
27261                 tag : 'div',
27262                 cls : 'btn-group roo-upload-cropbox-trash',
27263                 action : 'trash',
27264                 cn : [
27265                     {
27266                         tag : 'button',
27267                         cls : 'btn btn-default',
27268                         html : '<i class="fa fa-trash"></i>'
27269                     }
27270                 ]
27271             },
27272             {
27273                 tag : 'div',
27274                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27275                 action : 'rotate-right',
27276                 cn : [
27277                     {
27278                         tag : 'button',
27279                         cls : 'btn btn-default',
27280                         html : '<i class="fa fa-repeat"></i>'
27281                     }
27282                 ]
27283             }
27284         ],
27285         ROTATOR : [
27286             {
27287                 tag : 'div',
27288                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27289                 action : 'rotate-left',
27290                 cn : [
27291                     {
27292                         tag : 'button',
27293                         cls : 'btn btn-default',
27294                         html : '<i class="fa fa-undo"></i>'
27295                     }
27296                 ]
27297             },
27298             {
27299                 tag : 'div',
27300                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27301                 action : 'rotate-right',
27302                 cn : [
27303                     {
27304                         tag : 'button',
27305                         cls : 'btn btn-default',
27306                         html : '<i class="fa fa-repeat"></i>'
27307                     }
27308                 ]
27309             }
27310         ]
27311     }
27312 });
27313
27314 /*
27315 * Licence: LGPL
27316 */
27317
27318 /**
27319  * @class Roo.bootstrap.DocumentManager
27320  * @extends Roo.bootstrap.Component
27321  * Bootstrap DocumentManager class
27322  * @cfg {String} paramName default 'imageUpload'
27323  * @cfg {String} toolTipName default 'filename'
27324  * @cfg {String} method default POST
27325  * @cfg {String} url action url
27326  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27327  * @cfg {Boolean} multiple multiple upload default true
27328  * @cfg {Number} thumbSize default 300
27329  * @cfg {String} fieldLabel
27330  * @cfg {Number} labelWidth default 4
27331  * @cfg {String} labelAlign (left|top) default left
27332  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27333  * 
27334  * @constructor
27335  * Create a new DocumentManager
27336  * @param {Object} config The config object
27337  */
27338
27339 Roo.bootstrap.DocumentManager = function(config){
27340     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27341     
27342     this.files = [];
27343     this.delegates = [];
27344     
27345     this.addEvents({
27346         /**
27347          * @event initial
27348          * Fire when initial the DocumentManager
27349          * @param {Roo.bootstrap.DocumentManager} this
27350          */
27351         "initial" : true,
27352         /**
27353          * @event inspect
27354          * inspect selected file
27355          * @param {Roo.bootstrap.DocumentManager} this
27356          * @param {File} file
27357          */
27358         "inspect" : true,
27359         /**
27360          * @event exception
27361          * Fire when xhr load exception
27362          * @param {Roo.bootstrap.DocumentManager} this
27363          * @param {XMLHttpRequest} xhr
27364          */
27365         "exception" : true,
27366         /**
27367          * @event afterupload
27368          * Fire when xhr load exception
27369          * @param {Roo.bootstrap.DocumentManager} this
27370          * @param {XMLHttpRequest} xhr
27371          */
27372         "afterupload" : true,
27373         /**
27374          * @event prepare
27375          * prepare the form data
27376          * @param {Roo.bootstrap.DocumentManager} this
27377          * @param {Object} formData
27378          */
27379         "prepare" : true,
27380         /**
27381          * @event remove
27382          * Fire when remove the file
27383          * @param {Roo.bootstrap.DocumentManager} this
27384          * @param {Object} file
27385          */
27386         "remove" : true,
27387         /**
27388          * @event refresh
27389          * Fire after refresh the file
27390          * @param {Roo.bootstrap.DocumentManager} this
27391          */
27392         "refresh" : true,
27393         /**
27394          * @event click
27395          * Fire after click the image
27396          * @param {Roo.bootstrap.DocumentManager} this
27397          * @param {Object} file
27398          */
27399         "click" : true,
27400         /**
27401          * @event edit
27402          * Fire when upload a image and editable set to true
27403          * @param {Roo.bootstrap.DocumentManager} this
27404          * @param {Object} file
27405          */
27406         "edit" : true,
27407         /**
27408          * @event beforeselectfile
27409          * Fire before select file
27410          * @param {Roo.bootstrap.DocumentManager} this
27411          */
27412         "beforeselectfile" : true,
27413         /**
27414          * @event process
27415          * Fire before process file
27416          * @param {Roo.bootstrap.DocumentManager} this
27417          * @param {Object} file
27418          */
27419         "process" : true
27420         
27421     });
27422 };
27423
27424 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27425     
27426     boxes : 0,
27427     inputName : '',
27428     thumbSize : 300,
27429     multiple : true,
27430     files : false,
27431     method : 'POST',
27432     url : '',
27433     paramName : 'imageUpload',
27434     toolTipName : 'filename',
27435     fieldLabel : '',
27436     labelWidth : 4,
27437     labelAlign : 'left',
27438     editable : true,
27439     delegates : false,
27440     xhr : false, 
27441     
27442     getAutoCreate : function()
27443     {   
27444         var managerWidget = {
27445             tag : 'div',
27446             cls : 'roo-document-manager',
27447             cn : [
27448                 {
27449                     tag : 'input',
27450                     cls : 'roo-document-manager-selector',
27451                     type : 'file'
27452                 },
27453                 {
27454                     tag : 'div',
27455                     cls : 'roo-document-manager-uploader',
27456                     cn : [
27457                         {
27458                             tag : 'div',
27459                             cls : 'roo-document-manager-upload-btn',
27460                             html : '<i class="fa fa-plus"></i>'
27461                         }
27462                     ]
27463                     
27464                 }
27465             ]
27466         };
27467         
27468         var content = [
27469             {
27470                 tag : 'div',
27471                 cls : 'column col-md-12',
27472                 cn : managerWidget
27473             }
27474         ];
27475         
27476         if(this.fieldLabel.length){
27477             
27478             content = [
27479                 {
27480                     tag : 'div',
27481                     cls : 'column col-md-12',
27482                     html : this.fieldLabel
27483                 },
27484                 {
27485                     tag : 'div',
27486                     cls : 'column col-md-12',
27487                     cn : managerWidget
27488                 }
27489             ];
27490
27491             if(this.labelAlign == 'left'){
27492                 content = [
27493                     {
27494                         tag : 'div',
27495                         cls : 'column col-md-' + this.labelWidth,
27496                         html : this.fieldLabel
27497                     },
27498                     {
27499                         tag : 'div',
27500                         cls : 'column col-md-' + (12 - this.labelWidth),
27501                         cn : managerWidget
27502                     }
27503                 ];
27504                 
27505             }
27506         }
27507         
27508         var cfg = {
27509             tag : 'div',
27510             cls : 'row clearfix',
27511             cn : content
27512         };
27513         
27514         return cfg;
27515         
27516     },
27517     
27518     initEvents : function()
27519     {
27520         this.managerEl = this.el.select('.roo-document-manager', true).first();
27521         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27522         
27523         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27524         this.selectorEl.hide();
27525         
27526         if(this.multiple){
27527             this.selectorEl.attr('multiple', 'multiple');
27528         }
27529         
27530         this.selectorEl.on('change', this.onFileSelected, this);
27531         
27532         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27533         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27534         
27535         this.uploader.on('click', this.onUploaderClick, this);
27536         
27537         this.renderProgressDialog();
27538         
27539         var _this = this;
27540         
27541         window.addEventListener("resize", function() { _this.refresh(); } );
27542         
27543         this.fireEvent('initial', this);
27544     },
27545     
27546     renderProgressDialog : function()
27547     {
27548         var _this = this;
27549         
27550         this.progressDialog = new Roo.bootstrap.Modal({
27551             cls : 'roo-document-manager-progress-dialog',
27552             allow_close : false,
27553             title : '',
27554             buttons : [
27555                 {
27556                     name  :'cancel',
27557                     weight : 'danger',
27558                     html : 'Cancel'
27559                 }
27560             ], 
27561             listeners : { 
27562                 btnclick : function() {
27563                     _this.uploadCancel();
27564                     this.hide();
27565                 }
27566             }
27567         });
27568          
27569         this.progressDialog.render(Roo.get(document.body));
27570          
27571         this.progress = new Roo.bootstrap.Progress({
27572             cls : 'roo-document-manager-progress',
27573             active : true,
27574             striped : true
27575         });
27576         
27577         this.progress.render(this.progressDialog.getChildContainer());
27578         
27579         this.progressBar = new Roo.bootstrap.ProgressBar({
27580             cls : 'roo-document-manager-progress-bar',
27581             aria_valuenow : 0,
27582             aria_valuemin : 0,
27583             aria_valuemax : 12,
27584             panel : 'success'
27585         });
27586         
27587         this.progressBar.render(this.progress.getChildContainer());
27588     },
27589     
27590     onUploaderClick : function(e)
27591     {
27592         e.preventDefault();
27593      
27594         if(this.fireEvent('beforeselectfile', this) != false){
27595             this.selectorEl.dom.click();
27596         }
27597         
27598     },
27599     
27600     onFileSelected : function(e)
27601     {
27602         e.preventDefault();
27603         
27604         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27605             return;
27606         }
27607         
27608         Roo.each(this.selectorEl.dom.files, function(file){
27609             if(this.fireEvent('inspect', this, file) != false){
27610                 this.files.push(file);
27611             }
27612         }, this);
27613         
27614         this.queue();
27615         
27616     },
27617     
27618     queue : function()
27619     {
27620         this.selectorEl.dom.value = '';
27621         
27622         if(!this.files.length){
27623             return;
27624         }
27625         
27626         if(this.boxes > 0 && this.files.length > this.boxes){
27627             this.files = this.files.slice(0, this.boxes);
27628         }
27629         
27630         this.uploader.show();
27631         
27632         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27633             this.uploader.hide();
27634         }
27635         
27636         var _this = this;
27637         
27638         var files = [];
27639         
27640         var docs = [];
27641         
27642         Roo.each(this.files, function(file){
27643             
27644             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27645                 var f = this.renderPreview(file);
27646                 files.push(f);
27647                 return;
27648             }
27649             
27650             if(file.type.indexOf('image') != -1){
27651                 this.delegates.push(
27652                     (function(){
27653                         _this.process(file);
27654                     }).createDelegate(this)
27655                 );
27656         
27657                 return;
27658             }
27659             
27660             docs.push(
27661                 (function(){
27662                     _this.process(file);
27663                 }).createDelegate(this)
27664             );
27665             
27666         }, this);
27667         
27668         this.files = files;
27669         
27670         this.delegates = this.delegates.concat(docs);
27671         
27672         if(!this.delegates.length){
27673             this.refresh();
27674             return;
27675         }
27676         
27677         this.progressBar.aria_valuemax = this.delegates.length;
27678         
27679         this.arrange();
27680         
27681         return;
27682     },
27683     
27684     arrange : function()
27685     {
27686         if(!this.delegates.length){
27687             this.progressDialog.hide();
27688             this.refresh();
27689             return;
27690         }
27691         
27692         var delegate = this.delegates.shift();
27693         
27694         this.progressDialog.show();
27695         
27696         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27697         
27698         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27699         
27700         delegate();
27701     },
27702     
27703     refresh : function()
27704     {
27705         this.uploader.show();
27706         
27707         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27708             this.uploader.hide();
27709         }
27710         
27711         Roo.isTouch ? this.closable(false) : this.closable(true);
27712         
27713         this.fireEvent('refresh', this);
27714     },
27715     
27716     onRemove : function(e, el, o)
27717     {
27718         e.preventDefault();
27719         
27720         this.fireEvent('remove', this, o);
27721         
27722     },
27723     
27724     remove : function(o)
27725     {
27726         var files = [];
27727         
27728         Roo.each(this.files, function(file){
27729             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27730                 files.push(file);
27731                 return;
27732             }
27733
27734             o.target.remove();
27735
27736         }, this);
27737         
27738         this.files = files;
27739         
27740         this.refresh();
27741     },
27742     
27743     clear : function()
27744     {
27745         Roo.each(this.files, function(file){
27746             if(!file.target){
27747                 return;
27748             }
27749             
27750             file.target.remove();
27751
27752         }, this);
27753         
27754         this.files = [];
27755         
27756         this.refresh();
27757     },
27758     
27759     onClick : function(e, el, o)
27760     {
27761         e.preventDefault();
27762         
27763         this.fireEvent('click', this, o);
27764         
27765     },
27766     
27767     closable : function(closable)
27768     {
27769         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27770             
27771             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27772             
27773             if(closable){
27774                 el.show();
27775                 return;
27776             }
27777             
27778             el.hide();
27779             
27780         }, this);
27781     },
27782     
27783     xhrOnLoad : function(xhr)
27784     {
27785         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27786             el.remove();
27787         }, this);
27788         
27789         if (xhr.readyState !== 4) {
27790             this.arrange();
27791             this.fireEvent('exception', this, xhr);
27792             return;
27793         }
27794
27795         var response = Roo.decode(xhr.responseText);
27796         
27797         if(!response.success){
27798             this.arrange();
27799             this.fireEvent('exception', this, xhr);
27800             return;
27801         }
27802         
27803         var file = this.renderPreview(response.data);
27804         
27805         this.files.push(file);
27806         
27807         this.arrange();
27808         
27809         this.fireEvent('afterupload', this, xhr);
27810         
27811     },
27812     
27813     xhrOnError : function(xhr)
27814     {
27815         Roo.log('xhr on error');
27816         
27817         var response = Roo.decode(xhr.responseText);
27818           
27819         Roo.log(response);
27820         
27821         this.arrange();
27822     },
27823     
27824     process : function(file)
27825     {
27826         if(this.fireEvent('process', this, file) !== false){
27827             if(this.editable && file.type.indexOf('image') != -1){
27828                 this.fireEvent('edit', this, file);
27829                 return;
27830             }
27831
27832             this.uploadStart(file, false);
27833
27834             return;
27835         }
27836         
27837     },
27838     
27839     uploadStart : function(file, crop)
27840     {
27841         this.xhr = new XMLHttpRequest();
27842         
27843         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27844             this.arrange();
27845             return;
27846         }
27847         
27848         file.xhr = this.xhr;
27849             
27850         this.managerEl.createChild({
27851             tag : 'div',
27852             cls : 'roo-document-manager-loading',
27853             cn : [
27854                 {
27855                     tag : 'div',
27856                     tooltip : file.name,
27857                     cls : 'roo-document-manager-thumb',
27858                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27859                 }
27860             ]
27861
27862         });
27863
27864         this.xhr.open(this.method, this.url, true);
27865         
27866         var headers = {
27867             "Accept": "application/json",
27868             "Cache-Control": "no-cache",
27869             "X-Requested-With": "XMLHttpRequest"
27870         };
27871         
27872         for (var headerName in headers) {
27873             var headerValue = headers[headerName];
27874             if (headerValue) {
27875                 this.xhr.setRequestHeader(headerName, headerValue);
27876             }
27877         }
27878         
27879         var _this = this;
27880         
27881         this.xhr.onload = function()
27882         {
27883             _this.xhrOnLoad(_this.xhr);
27884         }
27885         
27886         this.xhr.onerror = function()
27887         {
27888             _this.xhrOnError(_this.xhr);
27889         }
27890         
27891         var formData = new FormData();
27892
27893         formData.append('returnHTML', 'NO');
27894         
27895         if(crop){
27896             formData.append('crop', crop);
27897         }
27898         
27899         formData.append(this.paramName, file, file.name);
27900         
27901         var options = {
27902             file : file, 
27903             manually : false
27904         };
27905         
27906         if(this.fireEvent('prepare', this, formData, options) != false){
27907             
27908             if(options.manually){
27909                 return;
27910             }
27911             
27912             this.xhr.send(formData);
27913             return;
27914         };
27915         
27916         this.uploadCancel();
27917     },
27918     
27919     uploadCancel : function()
27920     {
27921         if (this.xhr) {
27922             this.xhr.abort();
27923         }
27924         
27925         this.delegates = [];
27926         
27927         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27928             el.remove();
27929         }, this);
27930         
27931         this.arrange();
27932     },
27933     
27934     renderPreview : function(file)
27935     {
27936         if(typeof(file.target) != 'undefined' && file.target){
27937             return file;
27938         }
27939         
27940         var previewEl = this.managerEl.createChild({
27941             tag : 'div',
27942             cls : 'roo-document-manager-preview',
27943             cn : [
27944                 {
27945                     tag : 'div',
27946                     tooltip : file[this.toolTipName],
27947                     cls : 'roo-document-manager-thumb',
27948                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27949                 },
27950                 {
27951                     tag : 'button',
27952                     cls : 'close',
27953                     html : '<i class="fa fa-times-circle"></i>'
27954                 }
27955             ]
27956         });
27957
27958         var close = previewEl.select('button.close', true).first();
27959
27960         close.on('click', this.onRemove, this, file);
27961
27962         file.target = previewEl;
27963
27964         var image = previewEl.select('img', true).first();
27965         
27966         var _this = this;
27967         
27968         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27969         
27970         image.on('click', this.onClick, this, file);
27971         
27972         return file;
27973         
27974     },
27975     
27976     onPreviewLoad : function(file, image)
27977     {
27978         if(typeof(file.target) == 'undefined' || !file.target){
27979             return;
27980         }
27981         
27982         var width = image.dom.naturalWidth || image.dom.width;
27983         var height = image.dom.naturalHeight || image.dom.height;
27984         
27985         if(width > height){
27986             file.target.addClass('wide');
27987             return;
27988         }
27989         
27990         file.target.addClass('tall');
27991         return;
27992         
27993     },
27994     
27995     uploadFromSource : function(file, crop)
27996     {
27997         this.xhr = new XMLHttpRequest();
27998         
27999         this.managerEl.createChild({
28000             tag : 'div',
28001             cls : 'roo-document-manager-loading',
28002             cn : [
28003                 {
28004                     tag : 'div',
28005                     tooltip : file.name,
28006                     cls : 'roo-document-manager-thumb',
28007                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28008                 }
28009             ]
28010
28011         });
28012
28013         this.xhr.open(this.method, this.url, true);
28014         
28015         var headers = {
28016             "Accept": "application/json",
28017             "Cache-Control": "no-cache",
28018             "X-Requested-With": "XMLHttpRequest"
28019         };
28020         
28021         for (var headerName in headers) {
28022             var headerValue = headers[headerName];
28023             if (headerValue) {
28024                 this.xhr.setRequestHeader(headerName, headerValue);
28025             }
28026         }
28027         
28028         var _this = this;
28029         
28030         this.xhr.onload = function()
28031         {
28032             _this.xhrOnLoad(_this.xhr);
28033         }
28034         
28035         this.xhr.onerror = function()
28036         {
28037             _this.xhrOnError(_this.xhr);
28038         }
28039         
28040         var formData = new FormData();
28041
28042         formData.append('returnHTML', 'NO');
28043         
28044         formData.append('crop', crop);
28045         
28046         if(typeof(file.filename) != 'undefined'){
28047             formData.append('filename', file.filename);
28048         }
28049         
28050         if(typeof(file.mimetype) != 'undefined'){
28051             formData.append('mimetype', file.mimetype);
28052         }
28053         
28054         if(this.fireEvent('prepare', this, formData) != false){
28055             this.xhr.send(formData);
28056         };
28057     }
28058 });
28059
28060 /*
28061 * Licence: LGPL
28062 */
28063
28064 /**
28065  * @class Roo.bootstrap.DocumentViewer
28066  * @extends Roo.bootstrap.Component
28067  * Bootstrap DocumentViewer class
28068  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28069  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28070  * 
28071  * @constructor
28072  * Create a new DocumentViewer
28073  * @param {Object} config The config object
28074  */
28075
28076 Roo.bootstrap.DocumentViewer = function(config){
28077     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28078     
28079     this.addEvents({
28080         /**
28081          * @event initial
28082          * Fire after initEvent
28083          * @param {Roo.bootstrap.DocumentViewer} this
28084          */
28085         "initial" : true,
28086         /**
28087          * @event click
28088          * Fire after click
28089          * @param {Roo.bootstrap.DocumentViewer} this
28090          */
28091         "click" : true,
28092         /**
28093          * @event download
28094          * Fire after download button
28095          * @param {Roo.bootstrap.DocumentViewer} this
28096          */
28097         "download" : true,
28098         /**
28099          * @event trash
28100          * Fire after trash button
28101          * @param {Roo.bootstrap.DocumentViewer} this
28102          */
28103         "trash" : true
28104         
28105     });
28106 };
28107
28108 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28109     
28110     showDownload : true,
28111     
28112     showTrash : true,
28113     
28114     getAutoCreate : function()
28115     {
28116         var cfg = {
28117             tag : 'div',
28118             cls : 'roo-document-viewer',
28119             cn : [
28120                 {
28121                     tag : 'div',
28122                     cls : 'roo-document-viewer-body',
28123                     cn : [
28124                         {
28125                             tag : 'div',
28126                             cls : 'roo-document-viewer-thumb',
28127                             cn : [
28128                                 {
28129                                     tag : 'img',
28130                                     cls : 'roo-document-viewer-image'
28131                                 }
28132                             ]
28133                         }
28134                     ]
28135                 },
28136                 {
28137                     tag : 'div',
28138                     cls : 'roo-document-viewer-footer',
28139                     cn : {
28140                         tag : 'div',
28141                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28142                         cn : [
28143                             {
28144                                 tag : 'div',
28145                                 cls : 'btn-group roo-document-viewer-download',
28146                                 cn : [
28147                                     {
28148                                         tag : 'button',
28149                                         cls : 'btn btn-default',
28150                                         html : '<i class="fa fa-download"></i>'
28151                                     }
28152                                 ]
28153                             },
28154                             {
28155                                 tag : 'div',
28156                                 cls : 'btn-group roo-document-viewer-trash',
28157                                 cn : [
28158                                     {
28159                                         tag : 'button',
28160                                         cls : 'btn btn-default',
28161                                         html : '<i class="fa fa-trash"></i>'
28162                                     }
28163                                 ]
28164                             }
28165                         ]
28166                     }
28167                 }
28168             ]
28169         };
28170         
28171         return cfg;
28172     },
28173     
28174     initEvents : function()
28175     {
28176         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28177         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28178         
28179         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28180         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28181         
28182         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28183         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28184         
28185         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28186         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28187         
28188         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28189         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28190         
28191         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28192         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28193         
28194         this.bodyEl.on('click', this.onClick, this);
28195         this.downloadBtn.on('click', this.onDownload, this);
28196         this.trashBtn.on('click', this.onTrash, this);
28197         
28198         this.downloadBtn.hide();
28199         this.trashBtn.hide();
28200         
28201         if(this.showDownload){
28202             this.downloadBtn.show();
28203         }
28204         
28205         if(this.showTrash){
28206             this.trashBtn.show();
28207         }
28208         
28209         if(!this.showDownload && !this.showTrash) {
28210             this.footerEl.hide();
28211         }
28212         
28213     },
28214     
28215     initial : function()
28216     {
28217         this.fireEvent('initial', this);
28218         
28219     },
28220     
28221     onClick : function(e)
28222     {
28223         e.preventDefault();
28224         
28225         this.fireEvent('click', this);
28226     },
28227     
28228     onDownload : function(e)
28229     {
28230         e.preventDefault();
28231         
28232         this.fireEvent('download', this);
28233     },
28234     
28235     onTrash : function(e)
28236     {
28237         e.preventDefault();
28238         
28239         this.fireEvent('trash', this);
28240     }
28241     
28242 });
28243 /*
28244  * - LGPL
28245  *
28246  * nav progress bar
28247  * 
28248  */
28249
28250 /**
28251  * @class Roo.bootstrap.NavProgressBar
28252  * @extends Roo.bootstrap.Component
28253  * Bootstrap NavProgressBar class
28254  * 
28255  * @constructor
28256  * Create a new nav progress bar
28257  * @param {Object} config The config object
28258  */
28259
28260 Roo.bootstrap.NavProgressBar = function(config){
28261     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28262
28263     this.bullets = this.bullets || [];
28264    
28265 //    Roo.bootstrap.NavProgressBar.register(this);
28266      this.addEvents({
28267         /**
28268              * @event changed
28269              * Fires when the active item changes
28270              * @param {Roo.bootstrap.NavProgressBar} this
28271              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28272              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28273          */
28274         'changed': true
28275      });
28276     
28277 };
28278
28279 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28280     
28281     bullets : [],
28282     barItems : [],
28283     
28284     getAutoCreate : function()
28285     {
28286         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28287         
28288         cfg = {
28289             tag : 'div',
28290             cls : 'roo-navigation-bar-group',
28291             cn : [
28292                 {
28293                     tag : 'div',
28294                     cls : 'roo-navigation-top-bar'
28295                 },
28296                 {
28297                     tag : 'div',
28298                     cls : 'roo-navigation-bullets-bar',
28299                     cn : [
28300                         {
28301                             tag : 'ul',
28302                             cls : 'roo-navigation-bar'
28303                         }
28304                     ]
28305                 },
28306                 
28307                 {
28308                     tag : 'div',
28309                     cls : 'roo-navigation-bottom-bar'
28310                 }
28311             ]
28312             
28313         };
28314         
28315         return cfg;
28316         
28317     },
28318     
28319     initEvents: function() 
28320     {
28321         
28322     },
28323     
28324     onRender : function(ct, position) 
28325     {
28326         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28327         
28328         if(this.bullets.length){
28329             Roo.each(this.bullets, function(b){
28330                this.addItem(b);
28331             }, this);
28332         }
28333         
28334         this.format();
28335         
28336     },
28337     
28338     addItem : function(cfg)
28339     {
28340         var item = new Roo.bootstrap.NavProgressItem(cfg);
28341         
28342         item.parentId = this.id;
28343         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28344         
28345         if(cfg.html){
28346             var top = new Roo.bootstrap.Element({
28347                 tag : 'div',
28348                 cls : 'roo-navigation-bar-text'
28349             });
28350             
28351             var bottom = new Roo.bootstrap.Element({
28352                 tag : 'div',
28353                 cls : 'roo-navigation-bar-text'
28354             });
28355             
28356             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28357             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28358             
28359             var topText = new Roo.bootstrap.Element({
28360                 tag : 'span',
28361                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28362             });
28363             
28364             var bottomText = new Roo.bootstrap.Element({
28365                 tag : 'span',
28366                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28367             });
28368             
28369             topText.onRender(top.el, null);
28370             bottomText.onRender(bottom.el, null);
28371             
28372             item.topEl = top;
28373             item.bottomEl = bottom;
28374         }
28375         
28376         this.barItems.push(item);
28377         
28378         return item;
28379     },
28380     
28381     getActive : function()
28382     {
28383         var active = false;
28384         
28385         Roo.each(this.barItems, function(v){
28386             
28387             if (!v.isActive()) {
28388                 return;
28389             }
28390             
28391             active = v;
28392             return false;
28393             
28394         });
28395         
28396         return active;
28397     },
28398     
28399     setActiveItem : function(item)
28400     {
28401         var prev = false;
28402         
28403         Roo.each(this.barItems, function(v){
28404             if (v.rid == item.rid) {
28405                 return ;
28406             }
28407             
28408             if (v.isActive()) {
28409                 v.setActive(false);
28410                 prev = v;
28411             }
28412         });
28413
28414         item.setActive(true);
28415         
28416         this.fireEvent('changed', this, item, prev);
28417     },
28418     
28419     getBarItem: function(rid)
28420     {
28421         var ret = false;
28422         
28423         Roo.each(this.barItems, function(e) {
28424             if (e.rid != rid) {
28425                 return;
28426             }
28427             
28428             ret =  e;
28429             return false;
28430         });
28431         
28432         return ret;
28433     },
28434     
28435     indexOfItem : function(item)
28436     {
28437         var index = false;
28438         
28439         Roo.each(this.barItems, function(v, i){
28440             
28441             if (v.rid != item.rid) {
28442                 return;
28443             }
28444             
28445             index = i;
28446             return false
28447         });
28448         
28449         return index;
28450     },
28451     
28452     setActiveNext : function()
28453     {
28454         var i = this.indexOfItem(this.getActive());
28455         
28456         if (i > this.barItems.length) {
28457             return;
28458         }
28459         
28460         this.setActiveItem(this.barItems[i+1]);
28461     },
28462     
28463     setActivePrev : function()
28464     {
28465         var i = this.indexOfItem(this.getActive());
28466         
28467         if (i  < 1) {
28468             return;
28469         }
28470         
28471         this.setActiveItem(this.barItems[i-1]);
28472     },
28473     
28474     format : function()
28475     {
28476         if(!this.barItems.length){
28477             return;
28478         }
28479      
28480         var width = 100 / this.barItems.length;
28481         
28482         Roo.each(this.barItems, function(i){
28483             i.el.setStyle('width', width + '%');
28484             i.topEl.el.setStyle('width', width + '%');
28485             i.bottomEl.el.setStyle('width', width + '%');
28486         }, this);
28487         
28488     }
28489     
28490 });
28491 /*
28492  * - LGPL
28493  *
28494  * Nav Progress Item
28495  * 
28496  */
28497
28498 /**
28499  * @class Roo.bootstrap.NavProgressItem
28500  * @extends Roo.bootstrap.Component
28501  * Bootstrap NavProgressItem class
28502  * @cfg {String} rid the reference id
28503  * @cfg {Boolean} active (true|false) Is item active default false
28504  * @cfg {Boolean} disabled (true|false) Is item active default false
28505  * @cfg {String} html
28506  * @cfg {String} position (top|bottom) text position default bottom
28507  * @cfg {String} icon show icon instead of number
28508  * 
28509  * @constructor
28510  * Create a new NavProgressItem
28511  * @param {Object} config The config object
28512  */
28513 Roo.bootstrap.NavProgressItem = function(config){
28514     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28515     this.addEvents({
28516         // raw events
28517         /**
28518          * @event click
28519          * The raw click event for the entire grid.
28520          * @param {Roo.bootstrap.NavProgressItem} this
28521          * @param {Roo.EventObject} e
28522          */
28523         "click" : true
28524     });
28525    
28526 };
28527
28528 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28529     
28530     rid : '',
28531     active : false,
28532     disabled : false,
28533     html : '',
28534     position : 'bottom',
28535     icon : false,
28536     
28537     getAutoCreate : function()
28538     {
28539         var iconCls = 'roo-navigation-bar-item-icon';
28540         
28541         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28542         
28543         var cfg = {
28544             tag: 'li',
28545             cls: 'roo-navigation-bar-item',
28546             cn : [
28547                 {
28548                     tag : 'i',
28549                     cls : iconCls
28550                 }
28551             ]
28552         };
28553         
28554         if(this.active){
28555             cfg.cls += ' active';
28556         }
28557         if(this.disabled){
28558             cfg.cls += ' disabled';
28559         }
28560         
28561         return cfg;
28562     },
28563     
28564     disable : function()
28565     {
28566         this.setDisabled(true);
28567     },
28568     
28569     enable : function()
28570     {
28571         this.setDisabled(false);
28572     },
28573     
28574     initEvents: function() 
28575     {
28576         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28577         
28578         this.iconEl.on('click', this.onClick, this);
28579     },
28580     
28581     onClick : function(e)
28582     {
28583         e.preventDefault();
28584         
28585         if(this.disabled){
28586             return;
28587         }
28588         
28589         if(this.fireEvent('click', this, e) === false){
28590             return;
28591         };
28592         
28593         this.parent().setActiveItem(this);
28594     },
28595     
28596     isActive: function () 
28597     {
28598         return this.active;
28599     },
28600     
28601     setActive : function(state)
28602     {
28603         if(this.active == state){
28604             return;
28605         }
28606         
28607         this.active = state;
28608         
28609         if (state) {
28610             this.el.addClass('active');
28611             return;
28612         }
28613         
28614         this.el.removeClass('active');
28615         
28616         return;
28617     },
28618     
28619     setDisabled : function(state)
28620     {
28621         if(this.disabled == state){
28622             return;
28623         }
28624         
28625         this.disabled = state;
28626         
28627         if (state) {
28628             this.el.addClass('disabled');
28629             return;
28630         }
28631         
28632         this.el.removeClass('disabled');
28633     },
28634     
28635     tooltipEl : function()
28636     {
28637         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28638     }
28639 });
28640  
28641
28642  /*
28643  * - LGPL
28644  *
28645  * FieldLabel
28646  * 
28647  */
28648
28649 /**
28650  * @class Roo.bootstrap.FieldLabel
28651  * @extends Roo.bootstrap.Component
28652  * Bootstrap FieldLabel class
28653  * @cfg {String} html contents of the element
28654  * @cfg {String} tag tag of the element default label
28655  * @cfg {String} cls class of the element
28656  * @cfg {String} target label target 
28657  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28658  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28659  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28660  * @cfg {String} iconTooltip default "This field is required"
28661  * 
28662  * @constructor
28663  * Create a new FieldLabel
28664  * @param {Object} config The config object
28665  */
28666
28667 Roo.bootstrap.FieldLabel = function(config){
28668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28669     
28670     this.addEvents({
28671             /**
28672              * @event invalid
28673              * Fires after the field has been marked as invalid.
28674              * @param {Roo.form.FieldLabel} this
28675              * @param {String} msg The validation message
28676              */
28677             invalid : true,
28678             /**
28679              * @event valid
28680              * Fires after the field has been validated with no errors.
28681              * @param {Roo.form.FieldLabel} this
28682              */
28683             valid : true
28684         });
28685 };
28686
28687 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28688     
28689     tag: 'label',
28690     cls: '',
28691     html: '',
28692     target: '',
28693     allowBlank : true,
28694     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28695     validClass : 'text-success fa fa-lg fa-check',
28696     iconTooltip : 'This field is required',
28697     
28698     getAutoCreate : function(){
28699         
28700         var cfg = {
28701             tag : this.tag,
28702             cls : 'roo-bootstrap-field-label ' + this.cls,
28703             for : this.target,
28704             cn : [
28705                 {
28706                     tag : 'i',
28707                     cls : '',
28708                     tooltip : this.iconTooltip
28709                 },
28710                 {
28711                     tag : 'span',
28712                     html : this.html
28713                 }
28714             ] 
28715         };
28716         
28717         return cfg;
28718     },
28719     
28720     initEvents: function() 
28721     {
28722         Roo.bootstrap.Element.superclass.initEvents.call(this);
28723         
28724         this.iconEl = this.el.select('i', true).first();
28725         
28726         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28727         
28728         Roo.bootstrap.FieldLabel.register(this);
28729     },
28730     
28731     /**
28732      * Mark this field as valid
28733      */
28734     markValid : function()
28735     {
28736         this.iconEl.show();
28737         
28738         this.iconEl.removeClass(this.invalidClass);
28739         
28740         this.iconEl.addClass(this.validClass);
28741         
28742         this.fireEvent('valid', this);
28743     },
28744     
28745     /**
28746      * Mark this field as invalid
28747      * @param {String} msg The validation message
28748      */
28749     markInvalid : function(msg)
28750     {
28751         this.iconEl.show();
28752         
28753         this.iconEl.removeClass(this.validClass);
28754         
28755         this.iconEl.addClass(this.invalidClass);
28756         
28757         this.fireEvent('invalid', this, msg);
28758     }
28759     
28760    
28761 });
28762
28763 Roo.apply(Roo.bootstrap.FieldLabel, {
28764     
28765     groups: {},
28766     
28767      /**
28768     * register a FieldLabel Group
28769     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28770     */
28771     register : function(label)
28772     {
28773         if(this.groups.hasOwnProperty(label.target)){
28774             return;
28775         }
28776      
28777         this.groups[label.target] = label;
28778         
28779     },
28780     /**
28781     * fetch a FieldLabel Group based on the target
28782     * @param {string} target
28783     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28784     */
28785     get: function(target) {
28786         if (typeof(this.groups[target]) == 'undefined') {
28787             return false;
28788         }
28789         
28790         return this.groups[target] ;
28791     }
28792 });
28793
28794  
28795
28796  /*
28797  * - LGPL
28798  *
28799  * page DateSplitField.
28800  * 
28801  */
28802
28803
28804 /**
28805  * @class Roo.bootstrap.DateSplitField
28806  * @extends Roo.bootstrap.Component
28807  * Bootstrap DateSplitField class
28808  * @cfg {string} fieldLabel - the label associated
28809  * @cfg {Number} labelWidth set the width of label (0-12)
28810  * @cfg {String} labelAlign (top|left)
28811  * @cfg {Boolean} dayAllowBlank (true|false) default false
28812  * @cfg {Boolean} monthAllowBlank (true|false) default false
28813  * @cfg {Boolean} yearAllowBlank (true|false) default false
28814  * @cfg {string} dayPlaceholder 
28815  * @cfg {string} monthPlaceholder
28816  * @cfg {string} yearPlaceholder
28817  * @cfg {string} dayFormat default 'd'
28818  * @cfg {string} monthFormat default 'm'
28819  * @cfg {string} yearFormat default 'Y'
28820
28821  *     
28822  * @constructor
28823  * Create a new DateSplitField
28824  * @param {Object} config The config object
28825  */
28826
28827 Roo.bootstrap.DateSplitField = function(config){
28828     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28829     
28830     this.addEvents({
28831         // raw events
28832          /**
28833          * @event years
28834          * getting the data of years
28835          * @param {Roo.bootstrap.DateSplitField} this
28836          * @param {Object} years
28837          */
28838         "years" : true,
28839         /**
28840          * @event days
28841          * getting the data of days
28842          * @param {Roo.bootstrap.DateSplitField} this
28843          * @param {Object} days
28844          */
28845         "days" : true,
28846         /**
28847          * @event invalid
28848          * Fires after the field has been marked as invalid.
28849          * @param {Roo.form.Field} this
28850          * @param {String} msg The validation message
28851          */
28852         invalid : true,
28853        /**
28854          * @event valid
28855          * Fires after the field has been validated with no errors.
28856          * @param {Roo.form.Field} this
28857          */
28858         valid : true
28859     });
28860 };
28861
28862 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28863     
28864     fieldLabel : '',
28865     labelAlign : 'top',
28866     labelWidth : 3,
28867     dayAllowBlank : false,
28868     monthAllowBlank : false,
28869     yearAllowBlank : false,
28870     dayPlaceholder : '',
28871     monthPlaceholder : '',
28872     yearPlaceholder : '',
28873     dayFormat : 'd',
28874     monthFormat : 'm',
28875     yearFormat : 'Y',
28876     isFormField : true,
28877     
28878     getAutoCreate : function()
28879     {
28880         var cfg = {
28881             tag : 'div',
28882             cls : 'row roo-date-split-field-group',
28883             cn : [
28884                 {
28885                     tag : 'input',
28886                     type : 'hidden',
28887                     cls : 'form-hidden-field roo-date-split-field-group-value',
28888                     name : this.name
28889                 }
28890             ]
28891         };
28892         
28893         if(this.fieldLabel){
28894             cfg.cn.push({
28895                 tag : 'div',
28896                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28897                 cn : [
28898                     {
28899                         tag : 'label',
28900                         html : this.fieldLabel
28901                     }
28902                 ]
28903             });
28904         }
28905         
28906         Roo.each(['day', 'month', 'year'], function(t){
28907             cfg.cn.push({
28908                 tag : 'div',
28909                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28910             });
28911         }, this);
28912         
28913         return cfg;
28914     },
28915     
28916     inputEl: function ()
28917     {
28918         return this.el.select('.roo-date-split-field-group-value', true).first();
28919     },
28920     
28921     onRender : function(ct, position) 
28922     {
28923         var _this = this;
28924         
28925         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28926         
28927         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28928         
28929         this.dayField = new Roo.bootstrap.ComboBox({
28930             allowBlank : this.dayAllowBlank,
28931             alwaysQuery : true,
28932             displayField : 'value',
28933             editable : false,
28934             fieldLabel : '',
28935             forceSelection : true,
28936             mode : 'local',
28937             placeholder : this.dayPlaceholder,
28938             selectOnFocus : true,
28939             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28940             triggerAction : 'all',
28941             typeAhead : true,
28942             valueField : 'value',
28943             store : new Roo.data.SimpleStore({
28944                 data : (function() {    
28945                     var days = [];
28946                     _this.fireEvent('days', _this, days);
28947                     return days;
28948                 })(),
28949                 fields : [ 'value' ]
28950             }),
28951             listeners : {
28952                 select : function (_self, record, index)
28953                 {
28954                     _this.setValue(_this.getValue());
28955                 }
28956             }
28957         });
28958
28959         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28960         
28961         this.monthField = new Roo.bootstrap.MonthField({
28962             after : '<i class=\"fa fa-calendar\"></i>',
28963             allowBlank : this.monthAllowBlank,
28964             placeholder : this.monthPlaceholder,
28965             readOnly : true,
28966             listeners : {
28967                 render : function (_self)
28968                 {
28969                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28970                         e.preventDefault();
28971                         _self.focus();
28972                     });
28973                 },
28974                 select : function (_self, oldvalue, newvalue)
28975                 {
28976                     _this.setValue(_this.getValue());
28977                 }
28978             }
28979         });
28980         
28981         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28982         
28983         this.yearField = new Roo.bootstrap.ComboBox({
28984             allowBlank : this.yearAllowBlank,
28985             alwaysQuery : true,
28986             displayField : 'value',
28987             editable : false,
28988             fieldLabel : '',
28989             forceSelection : true,
28990             mode : 'local',
28991             placeholder : this.yearPlaceholder,
28992             selectOnFocus : true,
28993             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28994             triggerAction : 'all',
28995             typeAhead : true,
28996             valueField : 'value',
28997             store : new Roo.data.SimpleStore({
28998                 data : (function() {
28999                     var years = [];
29000                     _this.fireEvent('years', _this, years);
29001                     return years;
29002                 })(),
29003                 fields : [ 'value' ]
29004             }),
29005             listeners : {
29006                 select : function (_self, record, index)
29007                 {
29008                     _this.setValue(_this.getValue());
29009                 }
29010             }
29011         });
29012
29013         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29014     },
29015     
29016     setValue : function(v, format)
29017     {
29018         this.inputEl.dom.value = v;
29019         
29020         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29021         
29022         var d = Date.parseDate(v, f);
29023         
29024         if(!d){
29025             this.validate();
29026             return;
29027         }
29028         
29029         this.setDay(d.format(this.dayFormat));
29030         this.setMonth(d.format(this.monthFormat));
29031         this.setYear(d.format(this.yearFormat));
29032         
29033         this.validate();
29034         
29035         return;
29036     },
29037     
29038     setDay : function(v)
29039     {
29040         this.dayField.setValue(v);
29041         this.inputEl.dom.value = this.getValue();
29042         this.validate();
29043         return;
29044     },
29045     
29046     setMonth : function(v)
29047     {
29048         this.monthField.setValue(v, true);
29049         this.inputEl.dom.value = this.getValue();
29050         this.validate();
29051         return;
29052     },
29053     
29054     setYear : function(v)
29055     {
29056         this.yearField.setValue(v);
29057         this.inputEl.dom.value = this.getValue();
29058         this.validate();
29059         return;
29060     },
29061     
29062     getDay : function()
29063     {
29064         return this.dayField.getValue();
29065     },
29066     
29067     getMonth : function()
29068     {
29069         return this.monthField.getValue();
29070     },
29071     
29072     getYear : function()
29073     {
29074         return this.yearField.getValue();
29075     },
29076     
29077     getValue : function()
29078     {
29079         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29080         
29081         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29082         
29083         return date;
29084     },
29085     
29086     reset : function()
29087     {
29088         this.setDay('');
29089         this.setMonth('');
29090         this.setYear('');
29091         this.inputEl.dom.value = '';
29092         this.validate();
29093         return;
29094     },
29095     
29096     validate : function()
29097     {
29098         var d = this.dayField.validate();
29099         var m = this.monthField.validate();
29100         var y = this.yearField.validate();
29101         
29102         var valid = true;
29103         
29104         if(
29105                 (!this.dayAllowBlank && !d) ||
29106                 (!this.monthAllowBlank && !m) ||
29107                 (!this.yearAllowBlank && !y)
29108         ){
29109             valid = false;
29110         }
29111         
29112         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29113             return valid;
29114         }
29115         
29116         if(valid){
29117             this.markValid();
29118             return valid;
29119         }
29120         
29121         this.markInvalid();
29122         
29123         return valid;
29124     },
29125     
29126     markValid : function()
29127     {
29128         
29129         var label = this.el.select('label', true).first();
29130         var icon = this.el.select('i.fa-star', true).first();
29131
29132         if(label && icon){
29133             icon.remove();
29134         }
29135         
29136         this.fireEvent('valid', this);
29137     },
29138     
29139      /**
29140      * Mark this field as invalid
29141      * @param {String} msg The validation message
29142      */
29143     markInvalid : function(msg)
29144     {
29145         
29146         var label = this.el.select('label', true).first();
29147         var icon = this.el.select('i.fa-star', true).first();
29148
29149         if(label && !icon){
29150             this.el.select('.roo-date-split-field-label', true).createChild({
29151                 tag : 'i',
29152                 cls : 'text-danger fa fa-lg fa-star',
29153                 tooltip : 'This field is required',
29154                 style : 'margin-right:5px;'
29155             }, label, true);
29156         }
29157         
29158         this.fireEvent('invalid', this, msg);
29159     },
29160     
29161     clearInvalid : function()
29162     {
29163         var label = this.el.select('label', true).first();
29164         var icon = this.el.select('i.fa-star', true).first();
29165
29166         if(label && icon){
29167             icon.remove();
29168         }
29169         
29170         this.fireEvent('valid', this);
29171     },
29172     
29173     getName: function()
29174     {
29175         return this.name;
29176     }
29177     
29178 });
29179
29180  /**
29181  *
29182  * This is based on 
29183  * http://masonry.desandro.com
29184  *
29185  * The idea is to render all the bricks based on vertical width...
29186  *
29187  * The original code extends 'outlayer' - we might need to use that....
29188  * 
29189  */
29190
29191
29192 /**
29193  * @class Roo.bootstrap.LayoutMasonry
29194  * @extends Roo.bootstrap.Component
29195  * Bootstrap Layout Masonry class
29196  * 
29197  * @constructor
29198  * Create a new Element
29199  * @param {Object} config The config object
29200  */
29201
29202 Roo.bootstrap.LayoutMasonry = function(config){
29203     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29204     
29205     this.bricks = [];
29206     
29207 };
29208
29209 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29210     
29211     /**
29212      * @cfg {Boolean} isLayoutInstant = no animation?
29213      */   
29214     isLayoutInstant : false, // needed?
29215    
29216     /**
29217      * @cfg {Number} boxWidth  width of the columns
29218      */   
29219     boxWidth : 450,
29220     
29221       /**
29222      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29223      */   
29224     boxHeight : 0,
29225     
29226     /**
29227      * @cfg {Number} padWidth padding below box..
29228      */   
29229     padWidth : 10, 
29230     
29231     /**
29232      * @cfg {Number} gutter gutter width..
29233      */   
29234     gutter : 10,
29235     
29236      /**
29237      * @cfg {Number} maxCols maximum number of columns
29238      */   
29239     
29240     maxCols: 0,
29241     
29242     /**
29243      * @cfg {Boolean} isAutoInitial defalut true
29244      */   
29245     isAutoInitial : true, 
29246     
29247     containerWidth: 0,
29248     
29249     /**
29250      * @cfg {Boolean} isHorizontal defalut false
29251      */   
29252     isHorizontal : false, 
29253
29254     currentSize : null,
29255     
29256     tag: 'div',
29257     
29258     cls: '',
29259     
29260     bricks: null, //CompositeElement
29261     
29262     cols : 1,
29263     
29264     _isLayoutInited : false,
29265     
29266 //    isAlternative : false, // only use for vertical layout...
29267     
29268     /**
29269      * @cfg {Number} alternativePadWidth padding below box..
29270      */   
29271     alternativePadWidth : 50, 
29272     
29273     getAutoCreate : function(){
29274         
29275         var cfg = {
29276             tag: this.tag,
29277             cls: 'blog-masonary-wrapper ' + this.cls,
29278             cn : {
29279                 cls : 'mas-boxes masonary'
29280             }
29281         };
29282         
29283         return cfg;
29284     },
29285     
29286     getChildContainer: function( )
29287     {
29288         if (this.boxesEl) {
29289             return this.boxesEl;
29290         }
29291         
29292         this.boxesEl = this.el.select('.mas-boxes').first();
29293         
29294         return this.boxesEl;
29295     },
29296     
29297     
29298     initEvents : function()
29299     {
29300         var _this = this;
29301         
29302         if(this.isAutoInitial){
29303             Roo.log('hook children rendered');
29304             this.on('childrenrendered', function() {
29305                 Roo.log('children rendered');
29306                 _this.initial();
29307             } ,this);
29308         }
29309     },
29310     
29311     initial : function()
29312     {
29313         this.currentSize = this.el.getBox(true);
29314         
29315         Roo.EventManager.onWindowResize(this.resize, this); 
29316
29317         if(!this.isAutoInitial){
29318             this.layout();
29319             return;
29320         }
29321         
29322         this.layout();
29323         
29324         return;
29325         //this.layout.defer(500,this);
29326         
29327     },
29328     
29329     resize : function()
29330     {
29331         Roo.log('resize');
29332         
29333         var cs = this.el.getBox(true);
29334         
29335         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29336             Roo.log("no change in with or X");
29337             return;
29338         }
29339         
29340         this.currentSize = cs;
29341         
29342         this.layout();
29343         
29344     },
29345     
29346     layout : function()
29347     {   
29348         this._resetLayout();
29349         
29350         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29351         
29352         this.layoutItems( isInstant );
29353       
29354         this._isLayoutInited = true;
29355         
29356     },
29357     
29358     _resetLayout : function()
29359     {
29360         if(this.isHorizontal){
29361             this.horizontalMeasureColumns();
29362             return;
29363         }
29364         
29365         this.verticalMeasureColumns();
29366         
29367     },
29368     
29369     verticalMeasureColumns : function()
29370     {
29371         this.getContainerWidth();
29372         
29373 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29374 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29375 //            return;
29376 //        }
29377         
29378         var boxWidth = this.boxWidth + this.padWidth;
29379         
29380         if(this.containerWidth < this.boxWidth){
29381             boxWidth = this.containerWidth
29382         }
29383         
29384         var containerWidth = this.containerWidth;
29385         
29386         var cols = Math.floor(containerWidth / boxWidth);
29387         
29388         this.cols = Math.max( cols, 1 );
29389         
29390         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29391         
29392         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29393         
29394         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29395         
29396         this.colWidth = boxWidth + avail - this.padWidth;
29397         
29398         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29399         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29400     },
29401     
29402     horizontalMeasureColumns : function()
29403     {
29404         this.getContainerWidth();
29405         
29406         var boxWidth = this.boxWidth;
29407         
29408         if(this.containerWidth < boxWidth){
29409             boxWidth = this.containerWidth;
29410         }
29411         
29412         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29413         
29414         this.el.setHeight(boxWidth);
29415         
29416     },
29417     
29418     getContainerWidth : function()
29419     {
29420         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29421     },
29422     
29423     layoutItems : function( isInstant )
29424     {
29425         var items = Roo.apply([], this.bricks);
29426         
29427         if(this.isHorizontal){
29428             this._horizontalLayoutItems( items , isInstant );
29429             return;
29430         }
29431         
29432 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29433 //            this._verticalAlternativeLayoutItems( items , isInstant );
29434 //            return;
29435 //        }
29436         
29437         this._verticalLayoutItems( items , isInstant );
29438         
29439     },
29440     
29441     _verticalLayoutItems : function ( items , isInstant)
29442     {
29443         if ( !items || !items.length ) {
29444             return;
29445         }
29446         
29447         var standard = [
29448             ['xs', 'xs', 'xs', 'tall'],
29449             ['xs', 'xs', 'tall'],
29450             ['xs', 'xs', 'sm'],
29451             ['xs', 'xs', 'xs'],
29452             ['xs', 'tall'],
29453             ['xs', 'sm'],
29454             ['xs', 'xs'],
29455             ['xs'],
29456             
29457             ['sm', 'xs', 'xs'],
29458             ['sm', 'xs'],
29459             ['sm'],
29460             
29461             ['tall', 'xs', 'xs', 'xs'],
29462             ['tall', 'xs', 'xs'],
29463             ['tall', 'xs'],
29464             ['tall']
29465             
29466         ];
29467         
29468         var queue = [];
29469         
29470         var boxes = [];
29471         
29472         var box = [];
29473         
29474         Roo.each(items, function(item, k){
29475             
29476             switch (item.size) {
29477                 // these layouts take up a full box,
29478                 case 'md' :
29479                 case 'md-left' :
29480                 case 'md-right' :
29481                 case 'wide' :
29482                     
29483                     if(box.length){
29484                         boxes.push(box);
29485                         box = [];
29486                     }
29487                     
29488                     boxes.push([item]);
29489                     
29490                     break;
29491                     
29492                 case 'xs' :
29493                 case 'sm' :
29494                 case 'tall' :
29495                     
29496                     box.push(item);
29497                     
29498                     break;
29499                 default :
29500                     break;
29501                     
29502             }
29503             
29504         }, this);
29505         
29506         if(box.length){
29507             boxes.push(box);
29508             box = [];
29509         }
29510         
29511         var filterPattern = function(box, length)
29512         {
29513             if(!box.length){
29514                 return;
29515             }
29516             
29517             var match = false;
29518             
29519             var pattern = box.slice(0, length);
29520             
29521             var format = [];
29522             
29523             Roo.each(pattern, function(i){
29524                 format.push(i.size);
29525             }, this);
29526             
29527             Roo.each(standard, function(s){
29528                 
29529                 if(String(s) != String(format)){
29530                     return;
29531                 }
29532                 
29533                 match = true;
29534                 return false;
29535                 
29536             }, this);
29537             
29538             if(!match && length == 1){
29539                 return;
29540             }
29541             
29542             if(!match){
29543                 filterPattern(box, length - 1);
29544                 return;
29545             }
29546                 
29547             queue.push(pattern);
29548
29549             box = box.slice(length, box.length);
29550
29551             filterPattern(box, 4);
29552
29553             return;
29554             
29555         }
29556         
29557         Roo.each(boxes, function(box, k){
29558             
29559             if(!box.length){
29560                 return;
29561             }
29562             
29563             if(box.length == 1){
29564                 queue.push(box);
29565                 return;
29566             }
29567             
29568             filterPattern(box, 4);
29569             
29570         }, this);
29571         
29572         this._processVerticalLayoutQueue( queue, isInstant );
29573         
29574     },
29575     
29576 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29577 //    {
29578 //        if ( !items || !items.length ) {
29579 //            return;
29580 //        }
29581 //
29582 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29583 //        
29584 //    },
29585     
29586     _horizontalLayoutItems : function ( items , isInstant)
29587     {
29588         if ( !items || !items.length || items.length < 3) {
29589             return;
29590         }
29591         
29592         items.reverse();
29593         
29594         var eItems = items.slice(0, 3);
29595         
29596         items = items.slice(3, items.length);
29597         
29598         var standard = [
29599             ['xs', 'xs', 'xs', 'wide'],
29600             ['xs', 'xs', 'wide'],
29601             ['xs', 'xs', 'sm'],
29602             ['xs', 'xs', 'xs'],
29603             ['xs', 'wide'],
29604             ['xs', 'sm'],
29605             ['xs', 'xs'],
29606             ['xs'],
29607             
29608             ['sm', 'xs', 'xs'],
29609             ['sm', 'xs'],
29610             ['sm'],
29611             
29612             ['wide', 'xs', 'xs', 'xs'],
29613             ['wide', 'xs', 'xs'],
29614             ['wide', 'xs'],
29615             ['wide'],
29616             
29617             ['wide-thin']
29618         ];
29619         
29620         var queue = [];
29621         
29622         var boxes = [];
29623         
29624         var box = [];
29625         
29626         Roo.each(items, function(item, k){
29627             
29628             switch (item.size) {
29629                 case 'md' :
29630                 case 'md-left' :
29631                 case 'md-right' :
29632                 case 'tall' :
29633                     
29634                     if(box.length){
29635                         boxes.push(box);
29636                         box = [];
29637                     }
29638                     
29639                     boxes.push([item]);
29640                     
29641                     break;
29642                     
29643                 case 'xs' :
29644                 case 'sm' :
29645                 case 'wide' :
29646                 case 'wide-thin' :
29647                     
29648                     box.push(item);
29649                     
29650                     break;
29651                 default :
29652                     break;
29653                     
29654             }
29655             
29656         }, this);
29657         
29658         if(box.length){
29659             boxes.push(box);
29660             box = [];
29661         }
29662         
29663         var filterPattern = function(box, length)
29664         {
29665             if(!box.length){
29666                 return;
29667             }
29668             
29669             var match = false;
29670             
29671             var pattern = box.slice(0, length);
29672             
29673             var format = [];
29674             
29675             Roo.each(pattern, function(i){
29676                 format.push(i.size);
29677             }, this);
29678             
29679             Roo.each(standard, function(s){
29680                 
29681                 if(String(s) != String(format)){
29682                     return;
29683                 }
29684                 
29685                 match = true;
29686                 return false;
29687                 
29688             }, this);
29689             
29690             if(!match && length == 1){
29691                 return;
29692             }
29693             
29694             if(!match){
29695                 filterPattern(box, length - 1);
29696                 return;
29697             }
29698                 
29699             queue.push(pattern);
29700
29701             box = box.slice(length, box.length);
29702
29703             filterPattern(box, 4);
29704
29705             return;
29706             
29707         }
29708         
29709         Roo.each(boxes, function(box, k){
29710             
29711             if(!box.length){
29712                 return;
29713             }
29714             
29715             if(box.length == 1){
29716                 queue.push(box);
29717                 return;
29718             }
29719             
29720             filterPattern(box, 4);
29721             
29722         }, this);
29723         
29724         
29725         var prune = [];
29726         
29727         var pos = this.el.getBox(true);
29728         
29729         var minX = pos.x;
29730         
29731         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29732         
29733         var hit_end = false;
29734         
29735         Roo.each(queue, function(box){
29736             
29737             if(hit_end){
29738                 
29739                 Roo.each(box, function(b){
29740                 
29741                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29742                     b.el.hide();
29743
29744                 }, this);
29745
29746                 return;
29747             }
29748             
29749             var mx = 0;
29750             
29751             Roo.each(box, function(b){
29752                 
29753                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29754                 b.el.show();
29755
29756                 mx = Math.max(mx, b.x);
29757                 
29758             }, this);
29759             
29760             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29761             
29762             if(maxX < minX){
29763                 
29764                 Roo.each(box, function(b){
29765                 
29766                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29767                     b.el.hide();
29768                     
29769                 }, this);
29770                 
29771                 hit_end = true;
29772                 
29773                 return;
29774             }
29775             
29776             prune.push(box);
29777             
29778         }, this);
29779         
29780         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29781     },
29782     
29783     /** Sets position of item in DOM
29784     * @param {Element} item
29785     * @param {Number} x - horizontal position
29786     * @param {Number} y - vertical position
29787     * @param {Boolean} isInstant - disables transitions
29788     */
29789     _processVerticalLayoutQueue : function( queue, isInstant )
29790     {
29791         var pos = this.el.getBox(true);
29792         var x = pos.x;
29793         var y = pos.y;
29794         var maxY = [];
29795         
29796         for (var i = 0; i < this.cols; i++){
29797             maxY[i] = pos.y;
29798         }
29799         
29800         Roo.each(queue, function(box, k){
29801             
29802             var col = k % this.cols;
29803             
29804             Roo.each(box, function(b,kk){
29805                 
29806                 b.el.position('absolute');
29807                 
29808                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29809                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29810                 
29811                 if(b.size == 'md-left' || b.size == 'md-right'){
29812                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29813                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29814                 }
29815                 
29816                 b.el.setWidth(width);
29817                 b.el.setHeight(height);
29818                 // iframe?
29819                 b.el.select('iframe',true).setSize(width,height);
29820                 
29821             }, this);
29822             
29823             for (var i = 0; i < this.cols; i++){
29824                 
29825                 if(maxY[i] < maxY[col]){
29826                     col = i;
29827                     continue;
29828                 }
29829                 
29830                 col = Math.min(col, i);
29831                 
29832             }
29833             
29834             x = pos.x + col * (this.colWidth + this.padWidth);
29835             
29836             y = maxY[col];
29837             
29838             var positions = [];
29839             
29840             switch (box.length){
29841                 case 1 :
29842                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29843                     break;
29844                 case 2 :
29845                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29846                     break;
29847                 case 3 :
29848                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29849                     break;
29850                 case 4 :
29851                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29852                     break;
29853                 default :
29854                     break;
29855             }
29856             
29857             Roo.each(box, function(b,kk){
29858                 
29859                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29860                 
29861                 var sz = b.el.getSize();
29862                 
29863                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29864                 
29865             }, this);
29866             
29867         }, this);
29868         
29869         var mY = 0;
29870         
29871         for (var i = 0; i < this.cols; i++){
29872             mY = Math.max(mY, maxY[i]);
29873         }
29874         
29875         this.el.setHeight(mY - pos.y);
29876         
29877     },
29878     
29879 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29880 //    {
29881 //        var pos = this.el.getBox(true);
29882 //        var x = pos.x;
29883 //        var y = pos.y;
29884 //        var maxX = pos.right;
29885 //        
29886 //        var maxHeight = 0;
29887 //        
29888 //        Roo.each(items, function(item, k){
29889 //            
29890 //            var c = k % 2;
29891 //            
29892 //            item.el.position('absolute');
29893 //                
29894 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29895 //
29896 //            item.el.setWidth(width);
29897 //
29898 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29899 //
29900 //            item.el.setHeight(height);
29901 //            
29902 //            if(c == 0){
29903 //                item.el.setXY([x, y], isInstant ? false : true);
29904 //            } else {
29905 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29906 //            }
29907 //            
29908 //            y = y + height + this.alternativePadWidth;
29909 //            
29910 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29911 //            
29912 //        }, this);
29913 //        
29914 //        this.el.setHeight(maxHeight);
29915 //        
29916 //    },
29917     
29918     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29919     {
29920         var pos = this.el.getBox(true);
29921         
29922         var minX = pos.x;
29923         var minY = pos.y;
29924         
29925         var maxX = pos.right;
29926         
29927         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29928         
29929         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29930         
29931         Roo.each(queue, function(box, k){
29932             
29933             Roo.each(box, function(b, kk){
29934                 
29935                 b.el.position('absolute');
29936                 
29937                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29938                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29939                 
29940                 if(b.size == 'md-left' || b.size == 'md-right'){
29941                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29942                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29943                 }
29944                 
29945                 b.el.setWidth(width);
29946                 b.el.setHeight(height);
29947                 
29948             }, this);
29949             
29950             if(!box.length){
29951                 return;
29952             }
29953             
29954             var positions = [];
29955             
29956             switch (box.length){
29957                 case 1 :
29958                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29959                     break;
29960                 case 2 :
29961                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29962                     break;
29963                 case 3 :
29964                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29965                     break;
29966                 case 4 :
29967                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29968                     break;
29969                 default :
29970                     break;
29971             }
29972             
29973             Roo.each(box, function(b,kk){
29974                 
29975                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29976                 
29977                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29978                 
29979             }, this);
29980             
29981         }, this);
29982         
29983     },
29984     
29985     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29986     {
29987         Roo.each(eItems, function(b,k){
29988             
29989             b.size = (k == 0) ? 'sm' : 'xs';
29990             b.x = (k == 0) ? 2 : 1;
29991             b.y = (k == 0) ? 2 : 1;
29992             
29993             b.el.position('absolute');
29994             
29995             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29996                 
29997             b.el.setWidth(width);
29998             
29999             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30000             
30001             b.el.setHeight(height);
30002             
30003         }, this);
30004
30005         var positions = [];
30006         
30007         positions.push({
30008             x : maxX - this.unitWidth * 2 - this.gutter,
30009             y : minY
30010         });
30011         
30012         positions.push({
30013             x : maxX - this.unitWidth,
30014             y : minY + (this.unitWidth + this.gutter) * 2
30015         });
30016         
30017         positions.push({
30018             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30019             y : minY
30020         });
30021         
30022         Roo.each(eItems, function(b,k){
30023             
30024             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30025
30026         }, this);
30027         
30028     },
30029     
30030     getVerticalOneBoxColPositions : function(x, y, box)
30031     {
30032         var pos = [];
30033         
30034         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30035         
30036         if(box[0].size == 'md-left'){
30037             rand = 0;
30038         }
30039         
30040         if(box[0].size == 'md-right'){
30041             rand = 1;
30042         }
30043         
30044         pos.push({
30045             x : x + (this.unitWidth + this.gutter) * rand,
30046             y : y
30047         });
30048         
30049         return pos;
30050     },
30051     
30052     getVerticalTwoBoxColPositions : function(x, y, box)
30053     {
30054         var pos = [];
30055         
30056         if(box[0].size == 'xs'){
30057             
30058             pos.push({
30059                 x : x,
30060                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30061             });
30062
30063             pos.push({
30064                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30065                 y : y
30066             });
30067             
30068             return pos;
30069             
30070         }
30071         
30072         pos.push({
30073             x : x,
30074             y : y
30075         });
30076
30077         pos.push({
30078             x : x + (this.unitWidth + this.gutter) * 2,
30079             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30080         });
30081         
30082         return pos;
30083         
30084     },
30085     
30086     getVerticalThreeBoxColPositions : function(x, y, box)
30087     {
30088         var pos = [];
30089         
30090         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30091             
30092             pos.push({
30093                 x : x,
30094                 y : y
30095             });
30096
30097             pos.push({
30098                 x : x + (this.unitWidth + this.gutter) * 1,
30099                 y : y
30100             });
30101             
30102             pos.push({
30103                 x : x + (this.unitWidth + this.gutter) * 2,
30104                 y : y
30105             });
30106             
30107             return pos;
30108             
30109         }
30110         
30111         if(box[0].size == 'xs' && box[1].size == 'xs'){
30112             
30113             pos.push({
30114                 x : x,
30115                 y : y
30116             });
30117
30118             pos.push({
30119                 x : x,
30120                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30121             });
30122             
30123             pos.push({
30124                 x : x + (this.unitWidth + this.gutter) * 1,
30125                 y : y
30126             });
30127             
30128             return pos;
30129             
30130         }
30131         
30132         pos.push({
30133             x : x,
30134             y : y
30135         });
30136
30137         pos.push({
30138             x : x + (this.unitWidth + this.gutter) * 2,
30139             y : y
30140         });
30141
30142         pos.push({
30143             x : x + (this.unitWidth + this.gutter) * 2,
30144             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30145         });
30146             
30147         return pos;
30148         
30149     },
30150     
30151     getVerticalFourBoxColPositions : function(x, y, box)
30152     {
30153         var pos = [];
30154         
30155         if(box[0].size == 'xs'){
30156             
30157             pos.push({
30158                 x : x,
30159                 y : y
30160             });
30161
30162             pos.push({
30163                 x : x,
30164                 y : y + (this.unitHeight + this.gutter) * 1
30165             });
30166             
30167             pos.push({
30168                 x : x,
30169                 y : y + (this.unitHeight + this.gutter) * 2
30170             });
30171             
30172             pos.push({
30173                 x : x + (this.unitWidth + this.gutter) * 1,
30174                 y : y
30175             });
30176             
30177             return pos;
30178             
30179         }
30180         
30181         pos.push({
30182             x : x,
30183             y : y
30184         });
30185
30186         pos.push({
30187             x : x + (this.unitWidth + this.gutter) * 2,
30188             y : y
30189         });
30190
30191         pos.push({
30192             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30193             y : y + (this.unitHeight + this.gutter) * 1
30194         });
30195
30196         pos.push({
30197             x : x + (this.unitWidth + this.gutter) * 2,
30198             y : y + (this.unitWidth + this.gutter) * 2
30199         });
30200
30201         return pos;
30202         
30203     },
30204     
30205     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30206     {
30207         var pos = [];
30208         
30209         if(box[0].size == 'md-left'){
30210             pos.push({
30211                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30212                 y : minY
30213             });
30214             
30215             return pos;
30216         }
30217         
30218         if(box[0].size == 'md-right'){
30219             pos.push({
30220                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30221                 y : minY + (this.unitWidth + this.gutter) * 1
30222             });
30223             
30224             return pos;
30225         }
30226         
30227         var rand = Math.floor(Math.random() * (4 - box[0].y));
30228         
30229         pos.push({
30230             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30231             y : minY + (this.unitWidth + this.gutter) * rand
30232         });
30233         
30234         return pos;
30235         
30236     },
30237     
30238     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30239     {
30240         var pos = [];
30241         
30242         if(box[0].size == 'xs'){
30243             
30244             pos.push({
30245                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30246                 y : minY
30247             });
30248
30249             pos.push({
30250                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30251                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30252             });
30253             
30254             return pos;
30255             
30256         }
30257         
30258         pos.push({
30259             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30260             y : minY
30261         });
30262
30263         pos.push({
30264             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30265             y : minY + (this.unitWidth + this.gutter) * 2
30266         });
30267         
30268         return pos;
30269         
30270     },
30271     
30272     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30273     {
30274         var pos = [];
30275         
30276         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30277             
30278             pos.push({
30279                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30280                 y : minY
30281             });
30282
30283             pos.push({
30284                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30285                 y : minY + (this.unitWidth + this.gutter) * 1
30286             });
30287             
30288             pos.push({
30289                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30290                 y : minY + (this.unitWidth + this.gutter) * 2
30291             });
30292             
30293             return pos;
30294             
30295         }
30296         
30297         if(box[0].size == 'xs' && box[1].size == 'xs'){
30298             
30299             pos.push({
30300                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30301                 y : minY
30302             });
30303
30304             pos.push({
30305                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30306                 y : minY
30307             });
30308             
30309             pos.push({
30310                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30311                 y : minY + (this.unitWidth + this.gutter) * 1
30312             });
30313             
30314             return pos;
30315             
30316         }
30317         
30318         pos.push({
30319             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30320             y : minY
30321         });
30322
30323         pos.push({
30324             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30325             y : minY + (this.unitWidth + this.gutter) * 2
30326         });
30327
30328         pos.push({
30329             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30330             y : minY + (this.unitWidth + this.gutter) * 2
30331         });
30332             
30333         return pos;
30334         
30335     },
30336     
30337     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30338     {
30339         var pos = [];
30340         
30341         if(box[0].size == 'xs'){
30342             
30343             pos.push({
30344                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30345                 y : minY
30346             });
30347
30348             pos.push({
30349                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30350                 y : minY
30351             });
30352             
30353             pos.push({
30354                 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),
30355                 y : minY
30356             });
30357             
30358             pos.push({
30359                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30360                 y : minY + (this.unitWidth + this.gutter) * 1
30361             });
30362             
30363             return pos;
30364             
30365         }
30366         
30367         pos.push({
30368             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30369             y : minY
30370         });
30371         
30372         pos.push({
30373             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30374             y : minY + (this.unitWidth + this.gutter) * 2
30375         });
30376         
30377         pos.push({
30378             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30379             y : minY + (this.unitWidth + this.gutter) * 2
30380         });
30381         
30382         pos.push({
30383             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),
30384             y : minY + (this.unitWidth + this.gutter) * 2
30385         });
30386
30387         return pos;
30388         
30389     }
30390     
30391 });
30392
30393  
30394
30395  /**
30396  *
30397  * This is based on 
30398  * http://masonry.desandro.com
30399  *
30400  * The idea is to render all the bricks based on vertical width...
30401  *
30402  * The original code extends 'outlayer' - we might need to use that....
30403  * 
30404  */
30405
30406
30407 /**
30408  * @class Roo.bootstrap.LayoutMasonryAuto
30409  * @extends Roo.bootstrap.Component
30410  * Bootstrap Layout Masonry class
30411  * 
30412  * @constructor
30413  * Create a new Element
30414  * @param {Object} config The config object
30415  */
30416
30417 Roo.bootstrap.LayoutMasonryAuto = function(config){
30418     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30419 };
30420
30421 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30422     
30423       /**
30424      * @cfg {Boolean} isFitWidth  - resize the width..
30425      */   
30426     isFitWidth : false,  // options..
30427     /**
30428      * @cfg {Boolean} isOriginLeft = left align?
30429      */   
30430     isOriginLeft : true,
30431     /**
30432      * @cfg {Boolean} isOriginTop = top align?
30433      */   
30434     isOriginTop : false,
30435     /**
30436      * @cfg {Boolean} isLayoutInstant = no animation?
30437      */   
30438     isLayoutInstant : false, // needed?
30439     /**
30440      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30441      */   
30442     isResizingContainer : true,
30443     /**
30444      * @cfg {Number} columnWidth  width of the columns 
30445      */   
30446     
30447     columnWidth : 0,
30448     
30449     /**
30450      * @cfg {Number} maxCols maximum number of columns
30451      */   
30452     
30453     maxCols: 0,
30454     /**
30455      * @cfg {Number} padHeight padding below box..
30456      */   
30457     
30458     padHeight : 10, 
30459     
30460     /**
30461      * @cfg {Boolean} isAutoInitial defalut true
30462      */   
30463     
30464     isAutoInitial : true, 
30465     
30466     // private?
30467     gutter : 0,
30468     
30469     containerWidth: 0,
30470     initialColumnWidth : 0,
30471     currentSize : null,
30472     
30473     colYs : null, // array.
30474     maxY : 0,
30475     padWidth: 10,
30476     
30477     
30478     tag: 'div',
30479     cls: '',
30480     bricks: null, //CompositeElement
30481     cols : 0, // array?
30482     // element : null, // wrapped now this.el
30483     _isLayoutInited : null, 
30484     
30485     
30486     getAutoCreate : function(){
30487         
30488         var cfg = {
30489             tag: this.tag,
30490             cls: 'blog-masonary-wrapper ' + this.cls,
30491             cn : {
30492                 cls : 'mas-boxes masonary'
30493             }
30494         };
30495         
30496         return cfg;
30497     },
30498     
30499     getChildContainer: function( )
30500     {
30501         if (this.boxesEl) {
30502             return this.boxesEl;
30503         }
30504         
30505         this.boxesEl = this.el.select('.mas-boxes').first();
30506         
30507         return this.boxesEl;
30508     },
30509     
30510     
30511     initEvents : function()
30512     {
30513         var _this = this;
30514         
30515         if(this.isAutoInitial){
30516             Roo.log('hook children rendered');
30517             this.on('childrenrendered', function() {
30518                 Roo.log('children rendered');
30519                 _this.initial();
30520             } ,this);
30521         }
30522         
30523     },
30524     
30525     initial : function()
30526     {
30527         this.reloadItems();
30528
30529         this.currentSize = this.el.getBox(true);
30530
30531         /// was window resize... - let's see if this works..
30532         Roo.EventManager.onWindowResize(this.resize, this); 
30533
30534         if(!this.isAutoInitial){
30535             this.layout();
30536             return;
30537         }
30538         
30539         this.layout.defer(500,this);
30540     },
30541     
30542     reloadItems: function()
30543     {
30544         this.bricks = this.el.select('.masonry-brick', true);
30545         
30546         this.bricks.each(function(b) {
30547             //Roo.log(b.getSize());
30548             if (!b.attr('originalwidth')) {
30549                 b.attr('originalwidth',  b.getSize().width);
30550             }
30551             
30552         });
30553         
30554         Roo.log(this.bricks.elements.length);
30555     },
30556     
30557     resize : function()
30558     {
30559         Roo.log('resize');
30560         var cs = this.el.getBox(true);
30561         
30562         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30563             Roo.log("no change in with or X");
30564             return;
30565         }
30566         this.currentSize = cs;
30567         this.layout();
30568     },
30569     
30570     layout : function()
30571     {
30572          Roo.log('layout');
30573         this._resetLayout();
30574         //this._manageStamps();
30575       
30576         // don't animate first layout
30577         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30578         this.layoutItems( isInstant );
30579       
30580         // flag for initalized
30581         this._isLayoutInited = true;
30582     },
30583     
30584     layoutItems : function( isInstant )
30585     {
30586         //var items = this._getItemsForLayout( this.items );
30587         // original code supports filtering layout items.. we just ignore it..
30588         
30589         this._layoutItems( this.bricks , isInstant );
30590       
30591         this._postLayout();
30592     },
30593     _layoutItems : function ( items , isInstant)
30594     {
30595        //this.fireEvent( 'layout', this, items );
30596     
30597
30598         if ( !items || !items.elements.length ) {
30599           // no items, emit event with empty array
30600             return;
30601         }
30602
30603         var queue = [];
30604         items.each(function(item) {
30605             Roo.log("layout item");
30606             Roo.log(item);
30607             // get x/y object from method
30608             var position = this._getItemLayoutPosition( item );
30609             // enqueue
30610             position.item = item;
30611             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30612             queue.push( position );
30613         }, this);
30614       
30615         this._processLayoutQueue( queue );
30616     },
30617     /** Sets position of item in DOM
30618     * @param {Element} item
30619     * @param {Number} x - horizontal position
30620     * @param {Number} y - vertical position
30621     * @param {Boolean} isInstant - disables transitions
30622     */
30623     _processLayoutQueue : function( queue )
30624     {
30625         for ( var i=0, len = queue.length; i < len; i++ ) {
30626             var obj = queue[i];
30627             obj.item.position('absolute');
30628             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30629         }
30630     },
30631       
30632     
30633     /**
30634     * Any logic you want to do after each layout,
30635     * i.e. size the container
30636     */
30637     _postLayout : function()
30638     {
30639         this.resizeContainer();
30640     },
30641     
30642     resizeContainer : function()
30643     {
30644         if ( !this.isResizingContainer ) {
30645             return;
30646         }
30647         var size = this._getContainerSize();
30648         if ( size ) {
30649             this.el.setSize(size.width,size.height);
30650             this.boxesEl.setSize(size.width,size.height);
30651         }
30652     },
30653     
30654     
30655     
30656     _resetLayout : function()
30657     {
30658         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30659         this.colWidth = this.el.getWidth();
30660         //this.gutter = this.el.getWidth(); 
30661         
30662         this.measureColumns();
30663
30664         // reset column Y
30665         var i = this.cols;
30666         this.colYs = [];
30667         while (i--) {
30668             this.colYs.push( 0 );
30669         }
30670     
30671         this.maxY = 0;
30672     },
30673
30674     measureColumns : function()
30675     {
30676         this.getContainerWidth();
30677       // if columnWidth is 0, default to outerWidth of first item
30678         if ( !this.columnWidth ) {
30679             var firstItem = this.bricks.first();
30680             Roo.log(firstItem);
30681             this.columnWidth  = this.containerWidth;
30682             if (firstItem && firstItem.attr('originalwidth') ) {
30683                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30684             }
30685             // columnWidth fall back to item of first element
30686             Roo.log("set column width?");
30687                         this.initialColumnWidth = this.columnWidth  ;
30688
30689             // if first elem has no width, default to size of container
30690             
30691         }
30692         
30693         
30694         if (this.initialColumnWidth) {
30695             this.columnWidth = this.initialColumnWidth;
30696         }
30697         
30698         
30699             
30700         // column width is fixed at the top - however if container width get's smaller we should
30701         // reduce it...
30702         
30703         // this bit calcs how man columns..
30704             
30705         var columnWidth = this.columnWidth += this.gutter;
30706       
30707         // calculate columns
30708         var containerWidth = this.containerWidth + this.gutter;
30709         
30710         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30711         // fix rounding errors, typically with gutters
30712         var excess = columnWidth - containerWidth % columnWidth;
30713         
30714         
30715         // if overshoot is less than a pixel, round up, otherwise floor it
30716         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30717         cols = Math[ mathMethod ]( cols );
30718         this.cols = Math.max( cols, 1 );
30719         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30720         
30721          // padding positioning..
30722         var totalColWidth = this.cols * this.columnWidth;
30723         var padavail = this.containerWidth - totalColWidth;
30724         // so for 2 columns - we need 3 'pads'
30725         
30726         var padNeeded = (1+this.cols) * this.padWidth;
30727         
30728         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30729         
30730         this.columnWidth += padExtra
30731         //this.padWidth = Math.floor(padavail /  ( this.cols));
30732         
30733         // adjust colum width so that padding is fixed??
30734         
30735         // we have 3 columns ... total = width * 3
30736         // we have X left over... that should be used by 
30737         
30738         //if (this.expandC) {
30739             
30740         //}
30741         
30742         
30743         
30744     },
30745     
30746     getContainerWidth : function()
30747     {
30748        /* // container is parent if fit width
30749         var container = this.isFitWidth ? this.element.parentNode : this.element;
30750         // check that this.size and size are there
30751         // IE8 triggers resize on body size change, so they might not be
30752         
30753         var size = getSize( container );  //FIXME
30754         this.containerWidth = size && size.innerWidth; //FIXME
30755         */
30756          
30757         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30758         
30759     },
30760     
30761     _getItemLayoutPosition : function( item )  // what is item?
30762     {
30763         // we resize the item to our columnWidth..
30764       
30765         item.setWidth(this.columnWidth);
30766         item.autoBoxAdjust  = false;
30767         
30768         var sz = item.getSize();
30769  
30770         // how many columns does this brick span
30771         var remainder = this.containerWidth % this.columnWidth;
30772         
30773         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30774         // round if off by 1 pixel, otherwise use ceil
30775         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30776         colSpan = Math.min( colSpan, this.cols );
30777         
30778         // normally this should be '1' as we dont' currently allow multi width columns..
30779         
30780         var colGroup = this._getColGroup( colSpan );
30781         // get the minimum Y value from the columns
30782         var minimumY = Math.min.apply( Math, colGroup );
30783         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30784         
30785         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30786          
30787         // position the brick
30788         var position = {
30789             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30790             y: this.currentSize.y + minimumY + this.padHeight
30791         };
30792         
30793         Roo.log(position);
30794         // apply setHeight to necessary columns
30795         var setHeight = minimumY + sz.height + this.padHeight;
30796         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30797         
30798         var setSpan = this.cols + 1 - colGroup.length;
30799         for ( var i = 0; i < setSpan; i++ ) {
30800           this.colYs[ shortColIndex + i ] = setHeight ;
30801         }
30802       
30803         return position;
30804     },
30805     
30806     /**
30807      * @param {Number} colSpan - number of columns the element spans
30808      * @returns {Array} colGroup
30809      */
30810     _getColGroup : function( colSpan )
30811     {
30812         if ( colSpan < 2 ) {
30813           // if brick spans only one column, use all the column Ys
30814           return this.colYs;
30815         }
30816       
30817         var colGroup = [];
30818         // how many different places could this brick fit horizontally
30819         var groupCount = this.cols + 1 - colSpan;
30820         // for each group potential horizontal position
30821         for ( var i = 0; i < groupCount; i++ ) {
30822           // make an array of colY values for that one group
30823           var groupColYs = this.colYs.slice( i, i + colSpan );
30824           // and get the max value of the array
30825           colGroup[i] = Math.max.apply( Math, groupColYs );
30826         }
30827         return colGroup;
30828     },
30829     /*
30830     _manageStamp : function( stamp )
30831     {
30832         var stampSize =  stamp.getSize();
30833         var offset = stamp.getBox();
30834         // get the columns that this stamp affects
30835         var firstX = this.isOriginLeft ? offset.x : offset.right;
30836         var lastX = firstX + stampSize.width;
30837         var firstCol = Math.floor( firstX / this.columnWidth );
30838         firstCol = Math.max( 0, firstCol );
30839         
30840         var lastCol = Math.floor( lastX / this.columnWidth );
30841         // lastCol should not go over if multiple of columnWidth #425
30842         lastCol -= lastX % this.columnWidth ? 0 : 1;
30843         lastCol = Math.min( this.cols - 1, lastCol );
30844         
30845         // set colYs to bottom of the stamp
30846         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30847             stampSize.height;
30848             
30849         for ( var i = firstCol; i <= lastCol; i++ ) {
30850           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30851         }
30852     },
30853     */
30854     
30855     _getContainerSize : function()
30856     {
30857         this.maxY = Math.max.apply( Math, this.colYs );
30858         var size = {
30859             height: this.maxY
30860         };
30861       
30862         if ( this.isFitWidth ) {
30863             size.width = this._getContainerFitWidth();
30864         }
30865       
30866         return size;
30867     },
30868     
30869     _getContainerFitWidth : function()
30870     {
30871         var unusedCols = 0;
30872         // count unused columns
30873         var i = this.cols;
30874         while ( --i ) {
30875           if ( this.colYs[i] !== 0 ) {
30876             break;
30877           }
30878           unusedCols++;
30879         }
30880         // fit container to columns that have been used
30881         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30882     },
30883     
30884     needsResizeLayout : function()
30885     {
30886         var previousWidth = this.containerWidth;
30887         this.getContainerWidth();
30888         return previousWidth !== this.containerWidth;
30889     }
30890  
30891 });
30892
30893  
30894
30895  /*
30896  * - LGPL
30897  *
30898  * element
30899  * 
30900  */
30901
30902 /**
30903  * @class Roo.bootstrap.MasonryBrick
30904  * @extends Roo.bootstrap.Component
30905  * Bootstrap MasonryBrick class
30906  * 
30907  * @constructor
30908  * Create a new MasonryBrick
30909  * @param {Object} config The config object
30910  */
30911
30912 Roo.bootstrap.MasonryBrick = function(config){
30913     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30914     
30915     this.addEvents({
30916         // raw events
30917         /**
30918          * @event click
30919          * When a MasonryBrick is clcik
30920          * @param {Roo.bootstrap.MasonryBrick} this
30921          * @param {Roo.EventObject} e
30922          */
30923         "click" : true
30924     });
30925 };
30926
30927 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30928     
30929     /**
30930      * @cfg {String} title
30931      */   
30932     title : '',
30933     /**
30934      * @cfg {String} html
30935      */   
30936     html : '',
30937     /**
30938      * @cfg {String} bgimage
30939      */   
30940     bgimage : '',
30941     /**
30942      * @cfg {String} videourl
30943      */   
30944     videourl : '',
30945     /**
30946      * @cfg {String} cls
30947      */   
30948     cls : '',
30949     /**
30950      * @cfg {String} href
30951      */   
30952     href : '',
30953     /**
30954      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30955      */   
30956     size : 'xs',
30957     
30958     /**
30959      * @cfg {String} (center|bottom) placetitle
30960      */   
30961     placetitle : '',
30962     
30963     /**
30964      * @cfg {Boolean} isFitContainer defalut true
30965      */   
30966     isFitContainer : true, 
30967     
30968     /**
30969      * @cfg {Boolean} preventDefault defalut false
30970      */   
30971     preventDefault : false, 
30972     
30973     getAutoCreate : function()
30974     {
30975         if(!this.isFitContainer){
30976             return this.getSplitAutoCreate();
30977         }
30978         
30979         var cls = 'masonry-brick masonry-brick-full';
30980         
30981         if(this.href.length){
30982             cls += ' masonry-brick-link';
30983         }
30984         
30985         if(this.bgimage.length){
30986             cls += ' masonry-brick-image';
30987         }
30988         
30989         if(!this.html.length){
30990             cls += ' enable-mask';
30991         }
30992         
30993         if(this.size){
30994             cls += ' masonry-' + this.size + '-brick';
30995         }
30996         
30997         if(this.placetitle.length){
30998             
30999             switch (this.placetitle) {
31000                 case 'center' :
31001                     cls += ' masonry-center-title';
31002                     break;
31003                 case 'bottom' :
31004                     cls += ' masonry-bottom-title';
31005                     break;
31006                 default:
31007                     break;
31008             }
31009             
31010         } else {
31011             if(!this.html.length && !this.bgimage.length){
31012                 cls += ' masonry-center-title';
31013             }
31014
31015             if(!this.html.length && this.bgimage.length){
31016                 cls += ' masonry-bottom-title';
31017             }
31018         }
31019         
31020         if(this.cls){
31021             cls += ' ' + this.cls;
31022         }
31023         
31024         var cfg = {
31025             tag: (this.href.length) ? 'a' : 'div',
31026             cls: cls,
31027             cn: [
31028                 {
31029                     tag: 'div',
31030                     cls: 'masonry-brick-paragraph',
31031                     cn: []
31032                 }
31033             ]
31034         };
31035         
31036         if(this.href.length){
31037             cfg.href = this.href;
31038         }
31039         
31040         var cn = cfg.cn[0].cn;
31041         
31042         if(this.title.length){
31043             cn.push({
31044                 tag: 'h4',
31045                 cls: 'masonry-brick-title',
31046                 html: this.title
31047             });
31048         }
31049         
31050         if(this.html.length){
31051             cn.push({
31052                 tag: 'p',
31053                 cls: 'masonry-brick-text',
31054                 html: this.html
31055             });
31056         }  
31057         if (!this.title.length && !this.html.length) {
31058             cfg.cn[0].cls += ' hide';
31059         }
31060         
31061         if(this.bgimage.length){
31062             cfg.cn.push({
31063                 tag: 'img',
31064                 cls: 'masonry-brick-image-view',
31065                 src: this.bgimage
31066             });
31067         }
31068         
31069         if(this.videourl.length){
31070             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31071             // youtube support only?
31072             cfg.cn.push({
31073                 tag: 'iframe',
31074                 cls: 'masonry-brick-image-view',
31075                 src: vurl,
31076                 frameborder : 0,
31077                 allowfullscreen : true
31078             });
31079             
31080             
31081         }
31082         
31083         cfg.cn.push({
31084             tag: 'div',
31085             cls: 'masonry-brick-mask'
31086         });
31087         
31088         return cfg;
31089         
31090     },
31091     
31092     getSplitAutoCreate : function()
31093     {
31094         var cls = 'masonry-brick masonry-brick-split';
31095         
31096         if(this.href.length){
31097             cls += ' masonry-brick-link';
31098         }
31099         
31100         if(this.bgimage.length){
31101             cls += ' masonry-brick-image';
31102         }
31103         
31104         if(this.size){
31105             cls += ' masonry-' + this.size + '-brick';
31106         }
31107         
31108         switch (this.placetitle) {
31109             case 'center' :
31110                 cls += ' masonry-center-title';
31111                 break;
31112             case 'bottom' :
31113                 cls += ' masonry-bottom-title';
31114                 break;
31115             default:
31116                 if(!this.bgimage.length){
31117                     cls += ' masonry-center-title';
31118                 }
31119
31120                 if(this.bgimage.length){
31121                     cls += ' masonry-bottom-title';
31122                 }
31123                 break;
31124         }
31125         
31126         if(this.cls){
31127             cls += ' ' + this.cls;
31128         }
31129         
31130         var cfg = {
31131             tag: (this.href.length) ? 'a' : 'div',
31132             cls: cls,
31133             cn: [
31134                 {
31135                     tag: 'div',
31136                     cls: 'masonry-brick-split-head',
31137                     cn: [
31138                         {
31139                             tag: 'div',
31140                             cls: 'masonry-brick-paragraph',
31141                             cn: []
31142                         }
31143                     ]
31144                 },
31145                 {
31146                     tag: 'div',
31147                     cls: 'masonry-brick-split-body',
31148                     cn: []
31149                 }
31150             ]
31151         };
31152         
31153         if(this.href.length){
31154             cfg.href = this.href;
31155         }
31156         
31157         if(this.title.length){
31158             cfg.cn[0].cn[0].cn.push({
31159                 tag: 'h4',
31160                 cls: 'masonry-brick-title',
31161                 html: this.title
31162             });
31163         }
31164         
31165         if(this.html.length){
31166             cfg.cn[1].cn.push({
31167                 tag: 'p',
31168                 cls: 'masonry-brick-text',
31169                 html: this.html
31170             });
31171         }
31172
31173         if(this.bgimage.length){
31174             cfg.cn[0].cn.push({
31175                 tag: 'img',
31176                 cls: 'masonry-brick-image-view',
31177                 src: this.bgimage
31178             });
31179         }
31180         
31181         if(this.videourl.length){
31182             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31183             // youtube support only?
31184             cfg.cn[0].cn.cn.push({
31185                 tag: 'iframe',
31186                 cls: 'masonry-brick-image-view',
31187                 src: vurl,
31188                 frameborder : 0,
31189                 allowfullscreen : true
31190             });
31191         }
31192         
31193         return cfg;
31194     },
31195     
31196     initEvents: function() 
31197     {
31198         switch (this.size) {
31199             case 'xs' :
31200                 this.x = 1;
31201                 this.y = 1;
31202                 break;
31203             case 'sm' :
31204                 this.x = 2;
31205                 this.y = 2;
31206                 break;
31207             case 'md' :
31208             case 'md-left' :
31209             case 'md-right' :
31210                 this.x = 3;
31211                 this.y = 3;
31212                 break;
31213             case 'tall' :
31214                 this.x = 2;
31215                 this.y = 3;
31216                 break;
31217             case 'wide' :
31218                 this.x = 3;
31219                 this.y = 2;
31220                 break;
31221             case 'wide-thin' :
31222                 this.x = 3;
31223                 this.y = 1;
31224                 break;
31225                         
31226             default :
31227                 break;
31228         }
31229         
31230         if(Roo.isTouch){
31231             this.el.on('touchstart', this.onTouchStart, this);
31232             this.el.on('touchmove', this.onTouchMove, this);
31233             this.el.on('touchend', this.onTouchEnd, this);
31234             this.el.on('contextmenu', this.onContextMenu, this);
31235         } else {
31236             this.el.on('mouseenter'  ,this.enter, this);
31237             this.el.on('mouseleave', this.leave, this);
31238             this.el.on('click', this.onClick, this);
31239         }
31240         
31241         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31242             this.parent().bricks.push(this);   
31243         }
31244         
31245     },
31246     
31247     onClick: function(e, el)
31248     {
31249         var time = this.endTimer - this.startTimer;
31250         
31251         if(Roo.isTouch){
31252             if(time > 1000){
31253                 e.preventDefault();
31254                 return;
31255             }
31256         }
31257         
31258         if(!this.preventDefault){
31259             return;
31260         }
31261         
31262         e.preventDefault();
31263         this.fireEvent('click', this);
31264     },
31265     
31266     enter: function(e, el)
31267     {
31268         e.preventDefault();
31269         
31270         if(!this.isFitContainer){
31271             return;
31272         }
31273         
31274         if(this.bgimage.length && this.html.length){
31275             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31276         }
31277     },
31278     
31279     leave: function(e, el)
31280     {
31281         e.preventDefault();
31282         
31283         if(!this.isFitContainer){
31284             return;
31285         }
31286         
31287         if(this.bgimage.length && this.html.length){
31288             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31289         }
31290     },
31291     
31292     onTouchStart: function(e, el)
31293     {
31294 //        e.preventDefault();
31295         
31296         this.touchmoved = false;
31297         
31298         if(!this.isFitContainer){
31299             return;
31300         }
31301         
31302         if(!this.bgimage.length || !this.html.length){
31303             return;
31304         }
31305         
31306         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31307         
31308         this.timer = new Date().getTime();
31309         
31310     },
31311     
31312     onTouchMove: function(e, el)
31313     {
31314         this.touchmoved = true;
31315     },
31316     
31317     onContextMenu : function(e,el)
31318     {
31319         e.preventDefault();
31320         e.stopPropagation();
31321         return false;
31322     },
31323     
31324     onTouchEnd: function(e, el)
31325     {
31326 //        e.preventDefault();
31327         
31328         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31329         
31330             this.leave(e,el);
31331             
31332             return;
31333         }
31334         
31335         if(!this.bgimage.length || !this.html.length){
31336             
31337             if(this.href.length){
31338                 window.location.href = this.href;
31339             }
31340             
31341             return;
31342         }
31343         
31344         if(!this.isFitContainer){
31345             return;
31346         }
31347         
31348         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31349         
31350         window.location.href = this.href;
31351     }
31352     
31353 });
31354
31355  
31356
31357  /*
31358  * - LGPL
31359  *
31360  * element
31361  * 
31362  */
31363
31364 /**
31365  * @class Roo.bootstrap.Brick
31366  * @extends Roo.bootstrap.Component
31367  * Bootstrap Brick class
31368  * 
31369  * @constructor
31370  * Create a new Brick
31371  * @param {Object} config The config object
31372  */
31373
31374 Roo.bootstrap.Brick = function(config){
31375     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31376     
31377     this.addEvents({
31378         // raw events
31379         /**
31380          * @event click
31381          * When a Brick is click
31382          * @param {Roo.bootstrap.Brick} this
31383          * @param {Roo.EventObject} e
31384          */
31385         "click" : true
31386     });
31387 };
31388
31389 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31390     
31391     /**
31392      * @cfg {String} title
31393      */   
31394     title : '',
31395     /**
31396      * @cfg {String} html
31397      */   
31398     html : '',
31399     /**
31400      * @cfg {String} bgimage
31401      */   
31402     bgimage : '',
31403     /**
31404      * @cfg {String} cls
31405      */   
31406     cls : '',
31407     /**
31408      * @cfg {String} href
31409      */   
31410     href : '',
31411     /**
31412      * @cfg {String} video
31413      */   
31414     video : '',
31415     /**
31416      * @cfg {Boolean} square
31417      */   
31418     square : true,
31419     
31420     getAutoCreate : function()
31421     {
31422         var cls = 'roo-brick';
31423         
31424         if(this.href.length){
31425             cls += ' roo-brick-link';
31426         }
31427         
31428         if(this.bgimage.length){
31429             cls += ' roo-brick-image';
31430         }
31431         
31432         if(!this.html.length && !this.bgimage.length){
31433             cls += ' roo-brick-center-title';
31434         }
31435         
31436         if(!this.html.length && this.bgimage.length){
31437             cls += ' roo-brick-bottom-title';
31438         }
31439         
31440         if(this.cls){
31441             cls += ' ' + this.cls;
31442         }
31443         
31444         var cfg = {
31445             tag: (this.href.length) ? 'a' : 'div',
31446             cls: cls,
31447             cn: [
31448                 {
31449                     tag: 'div',
31450                     cls: 'roo-brick-paragraph',
31451                     cn: []
31452                 }
31453             ]
31454         };
31455         
31456         if(this.href.length){
31457             cfg.href = this.href;
31458         }
31459         
31460         var cn = cfg.cn[0].cn;
31461         
31462         if(this.title.length){
31463             cn.push({
31464                 tag: 'h4',
31465                 cls: 'roo-brick-title',
31466                 html: this.title
31467             });
31468         }
31469         
31470         if(this.html.length){
31471             cn.push({
31472                 tag: 'p',
31473                 cls: 'roo-brick-text',
31474                 html: this.html
31475             });
31476         } else {
31477             cn.cls += ' hide';
31478         }
31479         
31480         if(this.bgimage.length){
31481             cfg.cn.push({
31482                 tag: 'img',
31483                 cls: 'roo-brick-image-view',
31484                 src: this.bgimage
31485             });
31486         }
31487         
31488         return cfg;
31489     },
31490     
31491     initEvents: function() 
31492     {
31493         if(this.title.length || this.html.length){
31494             this.el.on('mouseenter'  ,this.enter, this);
31495             this.el.on('mouseleave', this.leave, this);
31496         }
31497         
31498         
31499         Roo.EventManager.onWindowResize(this.resize, this); 
31500         
31501         this.resize();
31502     },
31503     
31504     resize : function()
31505     {
31506         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31507         
31508         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31509         
31510         if(this.bgimage.length){
31511             var image = this.el.select('.roo-brick-image-view', true).first();
31512             image.setWidth(paragraph.getWidth());
31513             image.setHeight(paragraph.getWidth());
31514             
31515             this.el.setHeight(paragraph.getWidth());
31516             
31517         }
31518         
31519     },
31520     
31521     enter: function(e, el)
31522     {
31523         e.preventDefault();
31524         
31525         if(this.bgimage.length){
31526             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31527             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31528         }
31529     },
31530     
31531     leave: function(e, el)
31532     {
31533         e.preventDefault();
31534         
31535         if(this.bgimage.length){
31536             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31537             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31538         }
31539     }
31540     
31541 });
31542
31543  
31544
31545  /*
31546  * - LGPL
31547  *
31548  * Input
31549  * 
31550  */
31551
31552 /**
31553  * @class Roo.bootstrap.NumberField
31554  * @extends Roo.bootstrap.Input
31555  * Bootstrap NumberField class
31556  * 
31557  * 
31558  * 
31559  * 
31560  * @constructor
31561  * Create a new NumberField
31562  * @param {Object} config The config object
31563  */
31564
31565 Roo.bootstrap.NumberField = function(config){
31566     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31567 };
31568
31569 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31570     
31571     /**
31572      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31573      */
31574     allowDecimals : true,
31575     /**
31576      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31577      */
31578     decimalSeparator : ".",
31579     /**
31580      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31581      */
31582     decimalPrecision : 2,
31583     /**
31584      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31585      */
31586     allowNegative : true,
31587     /**
31588      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31589      */
31590     minValue : Number.NEGATIVE_INFINITY,
31591     /**
31592      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31593      */
31594     maxValue : Number.MAX_VALUE,
31595     /**
31596      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31597      */
31598     minText : "The minimum value for this field is {0}",
31599     /**
31600      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31601      */
31602     maxText : "The maximum value for this field is {0}",
31603     /**
31604      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31605      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31606      */
31607     nanText : "{0} is not a valid number",
31608     /**
31609      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31610      */
31611     castInt : true,
31612
31613     // private
31614     initEvents : function()
31615     {   
31616         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31617         
31618         var allowed = "0123456789";
31619         
31620         if(this.allowDecimals){
31621             allowed += this.decimalSeparator;
31622         }
31623         
31624         if(this.allowNegative){
31625             allowed += "-";
31626         }
31627         
31628         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31629         
31630         var keyPress = function(e){
31631             
31632             var k = e.getKey();
31633             
31634             var c = e.getCharCode();
31635             
31636             if(
31637                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31638                     allowed.indexOf(String.fromCharCode(c)) === -1
31639             ){
31640                 e.stopEvent();
31641                 return;
31642             }
31643             
31644             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31645                 return;
31646             }
31647             
31648             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31649                 e.stopEvent();
31650             }
31651         };
31652         
31653         this.el.on("keypress", keyPress, this);
31654     },
31655     
31656     validateValue : function(value)
31657     {
31658         
31659         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31660             return false;
31661         }
31662         
31663         var num = this.parseValue(value);
31664         
31665         if(isNaN(num)){
31666             this.markInvalid(String.format(this.nanText, value));
31667             return false;
31668         }
31669         
31670         if(num < this.minValue){
31671             this.markInvalid(String.format(this.minText, this.minValue));
31672             return false;
31673         }
31674         
31675         if(num > this.maxValue){
31676             this.markInvalid(String.format(this.maxText, this.maxValue));
31677             return false;
31678         }
31679         
31680         return true;
31681     },
31682
31683     getValue : function()
31684     {
31685         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31686     },
31687
31688     parseValue : function(value)
31689     {
31690         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31691         return isNaN(value) ? '' : value;
31692     },
31693
31694     fixPrecision : function(value)
31695     {
31696         var nan = isNaN(value);
31697         
31698         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31699             return nan ? '' : value;
31700         }
31701         return parseFloat(value).toFixed(this.decimalPrecision);
31702     },
31703
31704     setValue : function(v)
31705     {
31706         v = this.fixPrecision(v);
31707         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31708     },
31709
31710     decimalPrecisionFcn : function(v)
31711     {
31712         return Math.floor(v);
31713     },
31714
31715     beforeBlur : function()
31716     {
31717         if(!this.castInt){
31718             return;
31719         }
31720         
31721         var v = this.parseValue(this.getRawValue());
31722         if(v){
31723             this.setValue(v);
31724         }
31725     }
31726     
31727 });
31728
31729  
31730
31731 /*
31732 * Licence: LGPL
31733 */
31734
31735 /**
31736  * @class Roo.bootstrap.DocumentSlider
31737  * @extends Roo.bootstrap.Component
31738  * Bootstrap DocumentSlider class
31739  * 
31740  * @constructor
31741  * Create a new DocumentViewer
31742  * @param {Object} config The config object
31743  */
31744
31745 Roo.bootstrap.DocumentSlider = function(config){
31746     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31747     
31748     this.files = [];
31749     
31750     this.addEvents({
31751         /**
31752          * @event initial
31753          * Fire after initEvent
31754          * @param {Roo.bootstrap.DocumentSlider} this
31755          */
31756         "initial" : true,
31757         /**
31758          * @event update
31759          * Fire after update
31760          * @param {Roo.bootstrap.DocumentSlider} this
31761          */
31762         "update" : true,
31763         /**
31764          * @event click
31765          * Fire after click
31766          * @param {Roo.bootstrap.DocumentSlider} this
31767          */
31768         "click" : true
31769     });
31770 };
31771
31772 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31773     
31774     files : false,
31775     
31776     indicator : 0,
31777     
31778     getAutoCreate : function()
31779     {
31780         var cfg = {
31781             tag : 'div',
31782             cls : 'roo-document-slider',
31783             cn : [
31784                 {
31785                     tag : 'div',
31786                     cls : 'roo-document-slider-header',
31787                     cn : [
31788                         {
31789                             tag : 'div',
31790                             cls : 'roo-document-slider-header-title'
31791                         }
31792                     ]
31793                 },
31794                 {
31795                     tag : 'div',
31796                     cls : 'roo-document-slider-body',
31797                     cn : [
31798                         {
31799                             tag : 'div',
31800                             cls : 'roo-document-slider-prev',
31801                             cn : [
31802                                 {
31803                                     tag : 'i',
31804                                     cls : 'fa fa-chevron-left'
31805                                 }
31806                             ]
31807                         },
31808                         {
31809                             tag : 'div',
31810                             cls : 'roo-document-slider-thumb',
31811                             cn : [
31812                                 {
31813                                     tag : 'img',
31814                                     cls : 'roo-document-slider-image'
31815                                 }
31816                             ]
31817                         },
31818                         {
31819                             tag : 'div',
31820                             cls : 'roo-document-slider-next',
31821                             cn : [
31822                                 {
31823                                     tag : 'i',
31824                                     cls : 'fa fa-chevron-right'
31825                                 }
31826                             ]
31827                         }
31828                     ]
31829                 }
31830             ]
31831         };
31832         
31833         return cfg;
31834     },
31835     
31836     initEvents : function()
31837     {
31838         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31839         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31840         
31841         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31842         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31843         
31844         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31845         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31846         
31847         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31848         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31849         
31850         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31851         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31852         
31853         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31854         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31855         
31856         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31857         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31858         
31859         this.thumbEl.on('click', this.onClick, this);
31860         
31861         this.prevIndicator.on('click', this.prev, this);
31862         
31863         this.nextIndicator.on('click', this.next, this);
31864         
31865     },
31866     
31867     initial : function()
31868     {
31869         if(this.files.length){
31870             this.indicator = 1;
31871             this.update()
31872         }
31873         
31874         this.fireEvent('initial', this);
31875     },
31876     
31877     update : function()
31878     {
31879         this.imageEl.attr('src', this.files[this.indicator - 1]);
31880         
31881         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31882         
31883         this.prevIndicator.show();
31884         
31885         if(this.indicator == 1){
31886             this.prevIndicator.hide();
31887         }
31888         
31889         this.nextIndicator.show();
31890         
31891         if(this.indicator == this.files.length){
31892             this.nextIndicator.hide();
31893         }
31894         
31895         this.thumbEl.scrollTo('top');
31896         
31897         this.fireEvent('update', this);
31898     },
31899     
31900     onClick : function(e)
31901     {
31902         e.preventDefault();
31903         
31904         this.fireEvent('click', this);
31905     },
31906     
31907     prev : function(e)
31908     {
31909         e.preventDefault();
31910         
31911         this.indicator = Math.max(1, this.indicator - 1);
31912         
31913         this.update();
31914     },
31915     
31916     next : function(e)
31917     {
31918         e.preventDefault();
31919         
31920         this.indicator = Math.min(this.files.length, this.indicator + 1);
31921         
31922         this.update();
31923     }
31924 });
31925 /*
31926  * - LGPL
31927  *
31928  * RadioSet
31929  *
31930  *
31931  */
31932
31933 /**
31934  * @class Roo.bootstrap.RadioSet
31935  * @extends Roo.bootstrap.Input
31936  * Bootstrap RadioSet class
31937  * @cfg {String} indicatorpos (left|right) default left
31938  * @cfg {Boolean} inline (true|false) inline the element (default true)
31939  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
31940  * @constructor
31941  * Create a new RadioSet
31942  * @param {Object} config The config object
31943  */
31944
31945 Roo.bootstrap.RadioSet = function(config){
31946     
31947     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
31948
31949     this.radioes = [];
31950     
31951     Roo.bootstrap.RadioSet.register(this);
31952     
31953     this.addEvents({
31954         /**
31955         * @event check
31956         * Fires when the element is checked or unchecked.
31957         * @param {Roo.bootstrap.RadioSet} this This radio
31958         * @param {Roo.bootstrap.Radio} item The checked item
31959         */
31960        check : true
31961     });
31962     
31963 };
31964
31965 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
31966
31967     radioes : false,
31968     
31969     inline : true,
31970     
31971     weight : '',
31972     
31973     fieldLabel : '',
31974     
31975     indicatorpos : 'left',
31976     
31977     getAutoCreate : function()
31978     {
31979         var label = {
31980             tag : 'label',
31981             cls : 'roo-radio-set-label',
31982             cn : [
31983                 {
31984                     tag : 'span',
31985                     html : this.fieldLabel
31986                 }
31987             ]
31988         };
31989         
31990         if(this.indicatorpos == 'left'){
31991             label.cn.unshift({
31992                 tag : 'i',
31993                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
31994                 tooltip : 'This field is required'
31995             });
31996         } else {
31997             label.cn.push({
31998                 tag : 'i',
31999                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32000                 tooltip : 'This field is required'
32001             });
32002         }
32003         
32004         var items = {
32005             tag : 'div',
32006             cls : 'roo-radio-set-items'
32007         };
32008         
32009         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32010         
32011         if (align === 'left' && this.fieldLabel.length) {
32012             
32013             label.cls += ' col-md-' + this.labelWidth;
32014             
32015             items = {
32016                 cls : "col-md-" + (12 - this.labelWidth), 
32017                 cn: [
32018                     items
32019                 ]
32020             };
32021         }
32022         
32023         var cfg = {
32024             tag : 'div',
32025             cls : 'roo-radio-set',
32026             cn : [
32027                 {
32028                     tag : 'input',
32029                     cls : 'roo-radio-set-input',
32030                     type : 'text',
32031                     name : this.name,
32032                     value : this.value ? this.value :  ''
32033                 },
32034                 label,
32035                 items
32036             ]
32037         };
32038         
32039         if(this.inline) {
32040             cfg.cls += ' roo-radio-set-inline';
32041         }
32042         
32043         return cfg;
32044         
32045     },
32046
32047     initEvents : function()
32048     {
32049         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32050         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32051         
32052         if(!this.fieldLabel.length){
32053             this.labelEl.hide();
32054         }
32055         
32056         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32057         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32058         
32059         this.indicatorEl().hide();
32060         
32061         this.originalValue = this.getValue();
32062         
32063     },
32064     
32065     inputEl: function ()
32066     {
32067         return this.el.select('.roo-radio-set-input', true).first();
32068     },
32069     
32070     getChildContainer : function()
32071     {
32072         return this.itemsEl;
32073     },
32074     
32075     register : function(item)
32076     {
32077         this.radioes.push(item);
32078         
32079         if(this.inline){
32080             item.el.addClass('radio-inline');
32081         }
32082         
32083     },
32084     
32085     validate : function()
32086     {   
32087         var valid = false;
32088         
32089         Roo.each(this.radioes, function(i){
32090             if(!i.checked){
32091                 return;
32092             }
32093             
32094             valid = true;
32095             return false;
32096         });
32097         
32098         if(this.disabled || this.allowBlank || valid){
32099             this.markValid();
32100             return true;
32101         }
32102         
32103         this.markInvalid();
32104         return false;
32105         
32106     },
32107     
32108     markValid : function()
32109     {
32110         if(this.labelEl.isVisible(true)){
32111             this.indicatorEl().hide();
32112         }
32113         
32114         this.el.removeClass([this.invalidClass, this.validClass]);
32115         this.el.addClass(this.validClass);
32116         
32117         this.fireEvent('valid', this);
32118     },
32119     
32120     markInvalid : function(msg)
32121     {
32122         if(this.allowBlank || this.disabled){
32123             return;
32124         }
32125         
32126         if(this.labelEl.isVisible(true)){
32127             this.indicatorEl().show();
32128         }
32129         
32130         this.el.removeClass([this.invalidClass, this.validClass]);
32131         this.el.addClass(this.invalidClass);
32132         
32133         this.fireEvent('invalid', this, msg);
32134         
32135     },
32136     
32137     setValue : function(v, suppressEvent)
32138     {   
32139         Roo.each(this.radioes, function(i){
32140             
32141             i.checked = false;
32142             i.el.removeClass('checked');
32143             
32144             if(i.value === v || i.value.toString() === v.toString()){
32145                 i.checked = true;
32146                 i.el.addClass('checked');
32147                 
32148                 if(suppressEvent !== true){
32149                     this.fireEvent('check', this, i);
32150                 }
32151             }
32152             
32153         }, this);
32154         
32155         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32156         
32157     },
32158     
32159     clearInvalid : function(){
32160         
32161         if(!this.el || this.preventMark){
32162             return;
32163         }
32164         
32165         if(this.labelEl.isVisible(true)){
32166             this.indicatorEl().hide();
32167         }
32168         
32169         this.el.removeClass([this.invalidClass, this.validClass]);
32170         
32171         this.fireEvent('valid', this);
32172     }
32173     
32174 });
32175
32176 Roo.apply(Roo.bootstrap.RadioSet, {
32177     
32178     groups: {},
32179     
32180     register : function(set)
32181     {
32182         this.groups[set.name] = set;
32183     },
32184     
32185     get: function(name) 
32186     {
32187         if (typeof(this.groups[name]) == 'undefined') {
32188             return false;
32189         }
32190         
32191         return this.groups[name] ;
32192     }
32193     
32194 });
32195 /*
32196  * Based on:
32197  * Ext JS Library 1.1.1
32198  * Copyright(c) 2006-2007, Ext JS, LLC.
32199  *
32200  * Originally Released Under LGPL - original licence link has changed is not relivant.
32201  *
32202  * Fork - LGPL
32203  * <script type="text/javascript">
32204  */
32205
32206
32207 /**
32208  * @class Roo.bootstrap.SplitBar
32209  * @extends Roo.util.Observable
32210  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32211  * <br><br>
32212  * Usage:
32213  * <pre><code>
32214 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32215                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32216 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32217 split.minSize = 100;
32218 split.maxSize = 600;
32219 split.animate = true;
32220 split.on('moved', splitterMoved);
32221 </code></pre>
32222  * @constructor
32223  * Create a new SplitBar
32224  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32225  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32226  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32227  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32228                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32229                         position of the SplitBar).
32230  */
32231 Roo.bootstrap.SplitBar = function(cfg){
32232     
32233     /** @private */
32234     
32235     //{
32236     //  dragElement : elm
32237     //  resizingElement: el,
32238         // optional..
32239     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32240     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32241         // existingProxy ???
32242     //}
32243     
32244     this.el = Roo.get(cfg.dragElement, true);
32245     this.el.dom.unselectable = "on";
32246     /** @private */
32247     this.resizingEl = Roo.get(cfg.resizingElement, true);
32248
32249     /**
32250      * @private
32251      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32252      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32253      * @type Number
32254      */
32255     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32256     
32257     /**
32258      * The minimum size of the resizing element. (Defaults to 0)
32259      * @type Number
32260      */
32261     this.minSize = 0;
32262     
32263     /**
32264      * The maximum size of the resizing element. (Defaults to 2000)
32265      * @type Number
32266      */
32267     this.maxSize = 2000;
32268     
32269     /**
32270      * Whether to animate the transition to the new size
32271      * @type Boolean
32272      */
32273     this.animate = false;
32274     
32275     /**
32276      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32277      * @type Boolean
32278      */
32279     this.useShim = false;
32280     
32281     /** @private */
32282     this.shim = null;
32283     
32284     if(!cfg.existingProxy){
32285         /** @private */
32286         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32287     }else{
32288         this.proxy = Roo.get(cfg.existingProxy).dom;
32289     }
32290     /** @private */
32291     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32292     
32293     /** @private */
32294     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32295     
32296     /** @private */
32297     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32298     
32299     /** @private */
32300     this.dragSpecs = {};
32301     
32302     /**
32303      * @private The adapter to use to positon and resize elements
32304      */
32305     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32306     this.adapter.init(this);
32307     
32308     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32309         /** @private */
32310         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32311         this.el.addClass("roo-splitbar-h");
32312     }else{
32313         /** @private */
32314         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32315         this.el.addClass("roo-splitbar-v");
32316     }
32317     
32318     this.addEvents({
32319         /**
32320          * @event resize
32321          * Fires when the splitter is moved (alias for {@link #event-moved})
32322          * @param {Roo.bootstrap.SplitBar} this
32323          * @param {Number} newSize the new width or height
32324          */
32325         "resize" : true,
32326         /**
32327          * @event moved
32328          * Fires when the splitter is moved
32329          * @param {Roo.bootstrap.SplitBar} this
32330          * @param {Number} newSize the new width or height
32331          */
32332         "moved" : true,
32333         /**
32334          * @event beforeresize
32335          * Fires before the splitter is dragged
32336          * @param {Roo.bootstrap.SplitBar} this
32337          */
32338         "beforeresize" : true,
32339
32340         "beforeapply" : true
32341     });
32342
32343     Roo.util.Observable.call(this);
32344 };
32345
32346 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32347     onStartProxyDrag : function(x, y){
32348         this.fireEvent("beforeresize", this);
32349         if(!this.overlay){
32350             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32351             o.unselectable();
32352             o.enableDisplayMode("block");
32353             // all splitbars share the same overlay
32354             Roo.bootstrap.SplitBar.prototype.overlay = o;
32355         }
32356         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32357         this.overlay.show();
32358         Roo.get(this.proxy).setDisplayed("block");
32359         var size = this.adapter.getElementSize(this);
32360         this.activeMinSize = this.getMinimumSize();;
32361         this.activeMaxSize = this.getMaximumSize();;
32362         var c1 = size - this.activeMinSize;
32363         var c2 = Math.max(this.activeMaxSize - size, 0);
32364         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32365             this.dd.resetConstraints();
32366             this.dd.setXConstraint(
32367                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32368                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32369             );
32370             this.dd.setYConstraint(0, 0);
32371         }else{
32372             this.dd.resetConstraints();
32373             this.dd.setXConstraint(0, 0);
32374             this.dd.setYConstraint(
32375                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32376                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32377             );
32378          }
32379         this.dragSpecs.startSize = size;
32380         this.dragSpecs.startPoint = [x, y];
32381         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32382     },
32383     
32384     /** 
32385      * @private Called after the drag operation by the DDProxy
32386      */
32387     onEndProxyDrag : function(e){
32388         Roo.get(this.proxy).setDisplayed(false);
32389         var endPoint = Roo.lib.Event.getXY(e);
32390         if(this.overlay){
32391             this.overlay.hide();
32392         }
32393         var newSize;
32394         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32395             newSize = this.dragSpecs.startSize + 
32396                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32397                     endPoint[0] - this.dragSpecs.startPoint[0] :
32398                     this.dragSpecs.startPoint[0] - endPoint[0]
32399                 );
32400         }else{
32401             newSize = this.dragSpecs.startSize + 
32402                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32403                     endPoint[1] - this.dragSpecs.startPoint[1] :
32404                     this.dragSpecs.startPoint[1] - endPoint[1]
32405                 );
32406         }
32407         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32408         if(newSize != this.dragSpecs.startSize){
32409             if(this.fireEvent('beforeapply', this, newSize) !== false){
32410                 this.adapter.setElementSize(this, newSize);
32411                 this.fireEvent("moved", this, newSize);
32412                 this.fireEvent("resize", this, newSize);
32413             }
32414         }
32415     },
32416     
32417     /**
32418      * Get the adapter this SplitBar uses
32419      * @return The adapter object
32420      */
32421     getAdapter : function(){
32422         return this.adapter;
32423     },
32424     
32425     /**
32426      * Set the adapter this SplitBar uses
32427      * @param {Object} adapter A SplitBar adapter object
32428      */
32429     setAdapter : function(adapter){
32430         this.adapter = adapter;
32431         this.adapter.init(this);
32432     },
32433     
32434     /**
32435      * Gets the minimum size for the resizing element
32436      * @return {Number} The minimum size
32437      */
32438     getMinimumSize : function(){
32439         return this.minSize;
32440     },
32441     
32442     /**
32443      * Sets the minimum size for the resizing element
32444      * @param {Number} minSize The minimum size
32445      */
32446     setMinimumSize : function(minSize){
32447         this.minSize = minSize;
32448     },
32449     
32450     /**
32451      * Gets the maximum size for the resizing element
32452      * @return {Number} The maximum size
32453      */
32454     getMaximumSize : function(){
32455         return this.maxSize;
32456     },
32457     
32458     /**
32459      * Sets the maximum size for the resizing element
32460      * @param {Number} maxSize The maximum size
32461      */
32462     setMaximumSize : function(maxSize){
32463         this.maxSize = maxSize;
32464     },
32465     
32466     /**
32467      * Sets the initialize size for the resizing element
32468      * @param {Number} size The initial size
32469      */
32470     setCurrentSize : function(size){
32471         var oldAnimate = this.animate;
32472         this.animate = false;
32473         this.adapter.setElementSize(this, size);
32474         this.animate = oldAnimate;
32475     },
32476     
32477     /**
32478      * Destroy this splitbar. 
32479      * @param {Boolean} removeEl True to remove the element
32480      */
32481     destroy : function(removeEl){
32482         if(this.shim){
32483             this.shim.remove();
32484         }
32485         this.dd.unreg();
32486         this.proxy.parentNode.removeChild(this.proxy);
32487         if(removeEl){
32488             this.el.remove();
32489         }
32490     }
32491 });
32492
32493 /**
32494  * @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.
32495  */
32496 Roo.bootstrap.SplitBar.createProxy = function(dir){
32497     var proxy = new Roo.Element(document.createElement("div"));
32498     proxy.unselectable();
32499     var cls = 'roo-splitbar-proxy';
32500     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32501     document.body.appendChild(proxy.dom);
32502     return proxy.dom;
32503 };
32504
32505 /** 
32506  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32507  * Default Adapter. It assumes the splitter and resizing element are not positioned
32508  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32509  */
32510 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32511 };
32512
32513 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32514     // do nothing for now
32515     init : function(s){
32516     
32517     },
32518     /**
32519      * Called before drag operations to get the current size of the resizing element. 
32520      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32521      */
32522      getElementSize : function(s){
32523         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32524             return s.resizingEl.getWidth();
32525         }else{
32526             return s.resizingEl.getHeight();
32527         }
32528     },
32529     
32530     /**
32531      * Called after drag operations to set the size of the resizing element.
32532      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32533      * @param {Number} newSize The new size to set
32534      * @param {Function} onComplete A function to be invoked when resizing is complete
32535      */
32536     setElementSize : function(s, newSize, onComplete){
32537         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32538             if(!s.animate){
32539                 s.resizingEl.setWidth(newSize);
32540                 if(onComplete){
32541                     onComplete(s, newSize);
32542                 }
32543             }else{
32544                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32545             }
32546         }else{
32547             
32548             if(!s.animate){
32549                 s.resizingEl.setHeight(newSize);
32550                 if(onComplete){
32551                     onComplete(s, newSize);
32552                 }
32553             }else{
32554                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32555             }
32556         }
32557     }
32558 };
32559
32560 /** 
32561  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32562  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32563  * Adapter that  moves the splitter element to align with the resized sizing element. 
32564  * Used with an absolute positioned SplitBar.
32565  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32566  * document.body, make sure you assign an id to the body element.
32567  */
32568 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32569     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32570     this.container = Roo.get(container);
32571 };
32572
32573 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32574     init : function(s){
32575         this.basic.init(s);
32576     },
32577     
32578     getElementSize : function(s){
32579         return this.basic.getElementSize(s);
32580     },
32581     
32582     setElementSize : function(s, newSize, onComplete){
32583         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32584     },
32585     
32586     moveSplitter : function(s){
32587         var yes = Roo.bootstrap.SplitBar;
32588         switch(s.placement){
32589             case yes.LEFT:
32590                 s.el.setX(s.resizingEl.getRight());
32591                 break;
32592             case yes.RIGHT:
32593                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32594                 break;
32595             case yes.TOP:
32596                 s.el.setY(s.resizingEl.getBottom());
32597                 break;
32598             case yes.BOTTOM:
32599                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32600                 break;
32601         }
32602     }
32603 };
32604
32605 /**
32606  * Orientation constant - Create a vertical SplitBar
32607  * @static
32608  * @type Number
32609  */
32610 Roo.bootstrap.SplitBar.VERTICAL = 1;
32611
32612 /**
32613  * Orientation constant - Create a horizontal SplitBar
32614  * @static
32615  * @type Number
32616  */
32617 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32618
32619 /**
32620  * Placement constant - The resizing element is to the left of the splitter element
32621  * @static
32622  * @type Number
32623  */
32624 Roo.bootstrap.SplitBar.LEFT = 1;
32625
32626 /**
32627  * Placement constant - The resizing element is to the right of the splitter element
32628  * @static
32629  * @type Number
32630  */
32631 Roo.bootstrap.SplitBar.RIGHT = 2;
32632
32633 /**
32634  * Placement constant - The resizing element is positioned above the splitter element
32635  * @static
32636  * @type Number
32637  */
32638 Roo.bootstrap.SplitBar.TOP = 3;
32639
32640 /**
32641  * Placement constant - The resizing element is positioned under splitter element
32642  * @static
32643  * @type Number
32644  */
32645 Roo.bootstrap.SplitBar.BOTTOM = 4;
32646 Roo.namespace("Roo.bootstrap.layout");/*
32647  * Based on:
32648  * Ext JS Library 1.1.1
32649  * Copyright(c) 2006-2007, Ext JS, LLC.
32650  *
32651  * Originally Released Under LGPL - original licence link has changed is not relivant.
32652  *
32653  * Fork - LGPL
32654  * <script type="text/javascript">
32655  */
32656
32657 /**
32658  * @class Roo.bootstrap.layout.Manager
32659  * @extends Roo.bootstrap.Component
32660  * Base class for layout managers.
32661  */
32662 Roo.bootstrap.layout.Manager = function(config)
32663 {
32664     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32665
32666
32667
32668
32669
32670     /** false to disable window resize monitoring @type Boolean */
32671     this.monitorWindowResize = true;
32672     this.regions = {};
32673     this.addEvents({
32674         /**
32675          * @event layout
32676          * Fires when a layout is performed.
32677          * @param {Roo.LayoutManager} this
32678          */
32679         "layout" : true,
32680         /**
32681          * @event regionresized
32682          * Fires when the user resizes a region.
32683          * @param {Roo.LayoutRegion} region The resized region
32684          * @param {Number} newSize The new size (width for east/west, height for north/south)
32685          */
32686         "regionresized" : true,
32687         /**
32688          * @event regioncollapsed
32689          * Fires when a region is collapsed.
32690          * @param {Roo.LayoutRegion} region The collapsed region
32691          */
32692         "regioncollapsed" : true,
32693         /**
32694          * @event regionexpanded
32695          * Fires when a region is expanded.
32696          * @param {Roo.LayoutRegion} region The expanded region
32697          */
32698         "regionexpanded" : true
32699     });
32700     this.updating = false;
32701
32702     if (config.el) {
32703         this.el = Roo.get(config.el);
32704         this.initEvents();
32705     }
32706
32707 };
32708
32709 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32710
32711
32712     regions : null,
32713
32714     monitorWindowResize : true,
32715
32716
32717     updating : false,
32718
32719
32720     onRender : function(ct, position)
32721     {
32722         if(!this.el){
32723             this.el = Roo.get(ct);
32724             this.initEvents();
32725         }
32726         //this.fireEvent('render',this);
32727     },
32728
32729
32730     initEvents: function()
32731     {
32732
32733
32734         // ie scrollbar fix
32735         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32736             document.body.scroll = "no";
32737         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32738             this.el.position('relative');
32739         }
32740         this.id = this.el.id;
32741         this.el.addClass("roo-layout-container");
32742         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32743         if(this.el.dom != document.body ) {
32744             this.el.on('resize', this.layout,this);
32745             this.el.on('show', this.layout,this);
32746         }
32747
32748     },
32749
32750     /**
32751      * Returns true if this layout is currently being updated
32752      * @return {Boolean}
32753      */
32754     isUpdating : function(){
32755         return this.updating;
32756     },
32757
32758     /**
32759      * Suspend the LayoutManager from doing auto-layouts while
32760      * making multiple add or remove calls
32761      */
32762     beginUpdate : function(){
32763         this.updating = true;
32764     },
32765
32766     /**
32767      * Restore auto-layouts and optionally disable the manager from performing a layout
32768      * @param {Boolean} noLayout true to disable a layout update
32769      */
32770     endUpdate : function(noLayout){
32771         this.updating = false;
32772         if(!noLayout){
32773             this.layout();
32774         }
32775     },
32776
32777     layout: function(){
32778         // abstract...
32779     },
32780
32781     onRegionResized : function(region, newSize){
32782         this.fireEvent("regionresized", region, newSize);
32783         this.layout();
32784     },
32785
32786     onRegionCollapsed : function(region){
32787         this.fireEvent("regioncollapsed", region);
32788     },
32789
32790     onRegionExpanded : function(region){
32791         this.fireEvent("regionexpanded", region);
32792     },
32793
32794     /**
32795      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32796      * performs box-model adjustments.
32797      * @return {Object} The size as an object {width: (the width), height: (the height)}
32798      */
32799     getViewSize : function()
32800     {
32801         var size;
32802         if(this.el.dom != document.body){
32803             size = this.el.getSize();
32804         }else{
32805             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32806         }
32807         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32808         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32809         return size;
32810     },
32811
32812     /**
32813      * Returns the Element this layout is bound to.
32814      * @return {Roo.Element}
32815      */
32816     getEl : function(){
32817         return this.el;
32818     },
32819
32820     /**
32821      * Returns the specified region.
32822      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32823      * @return {Roo.LayoutRegion}
32824      */
32825     getRegion : function(target){
32826         return this.regions[target.toLowerCase()];
32827     },
32828
32829     onWindowResize : function(){
32830         if(this.monitorWindowResize){
32831             this.layout();
32832         }
32833     }
32834 });
32835 /*
32836  * Based on:
32837  * Ext JS Library 1.1.1
32838  * Copyright(c) 2006-2007, Ext JS, LLC.
32839  *
32840  * Originally Released Under LGPL - original licence link has changed is not relivant.
32841  *
32842  * Fork - LGPL
32843  * <script type="text/javascript">
32844  */
32845 /**
32846  * @class Roo.bootstrap.layout.Border
32847  * @extends Roo.bootstrap.layout.Manager
32848  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32849  * please see: examples/bootstrap/nested.html<br><br>
32850  
32851 <b>The container the layout is rendered into can be either the body element or any other element.
32852 If it is not the body element, the container needs to either be an absolute positioned element,
32853 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32854 the container size if it is not the body element.</b>
32855
32856 * @constructor
32857 * Create a new Border
32858 * @param {Object} config Configuration options
32859  */
32860 Roo.bootstrap.layout.Border = function(config){
32861     config = config || {};
32862     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32863     
32864     
32865     
32866     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32867         if(config[region]){
32868             config[region].region = region;
32869             this.addRegion(config[region]);
32870         }
32871     },this);
32872     
32873 };
32874
32875 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32876
32877 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32878     /**
32879      * Creates and adds a new region if it doesn't already exist.
32880      * @param {String} target The target region key (north, south, east, west or center).
32881      * @param {Object} config The regions config object
32882      * @return {BorderLayoutRegion} The new region
32883      */
32884     addRegion : function(config)
32885     {
32886         if(!this.regions[config.region]){
32887             var r = this.factory(config);
32888             this.bindRegion(r);
32889         }
32890         return this.regions[config.region];
32891     },
32892
32893     // private (kinda)
32894     bindRegion : function(r){
32895         this.regions[r.config.region] = r;
32896         
32897         r.on("visibilitychange",    this.layout, this);
32898         r.on("paneladded",          this.layout, this);
32899         r.on("panelremoved",        this.layout, this);
32900         r.on("invalidated",         this.layout, this);
32901         r.on("resized",             this.onRegionResized, this);
32902         r.on("collapsed",           this.onRegionCollapsed, this);
32903         r.on("expanded",            this.onRegionExpanded, this);
32904     },
32905
32906     /**
32907      * Performs a layout update.
32908      */
32909     layout : function()
32910     {
32911         if(this.updating) {
32912             return;
32913         }
32914         
32915         // render all the rebions if they have not been done alreayd?
32916         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32917             if(this.regions[region] && !this.regions[region].bodyEl){
32918                 this.regions[region].onRender(this.el)
32919             }
32920         },this);
32921         
32922         var size = this.getViewSize();
32923         var w = size.width;
32924         var h = size.height;
32925         var centerW = w;
32926         var centerH = h;
32927         var centerY = 0;
32928         var centerX = 0;
32929         //var x = 0, y = 0;
32930
32931         var rs = this.regions;
32932         var north = rs["north"];
32933         var south = rs["south"]; 
32934         var west = rs["west"];
32935         var east = rs["east"];
32936         var center = rs["center"];
32937         //if(this.hideOnLayout){ // not supported anymore
32938             //c.el.setStyle("display", "none");
32939         //}
32940         if(north && north.isVisible()){
32941             var b = north.getBox();
32942             var m = north.getMargins();
32943             b.width = w - (m.left+m.right);
32944             b.x = m.left;
32945             b.y = m.top;
32946             centerY = b.height + b.y + m.bottom;
32947             centerH -= centerY;
32948             north.updateBox(this.safeBox(b));
32949         }
32950         if(south && south.isVisible()){
32951             var b = south.getBox();
32952             var m = south.getMargins();
32953             b.width = w - (m.left+m.right);
32954             b.x = m.left;
32955             var totalHeight = (b.height + m.top + m.bottom);
32956             b.y = h - totalHeight + m.top;
32957             centerH -= totalHeight;
32958             south.updateBox(this.safeBox(b));
32959         }
32960         if(west && west.isVisible()){
32961             var b = west.getBox();
32962             var m = west.getMargins();
32963             b.height = centerH - (m.top+m.bottom);
32964             b.x = m.left;
32965             b.y = centerY + m.top;
32966             var totalWidth = (b.width + m.left + m.right);
32967             centerX += totalWidth;
32968             centerW -= totalWidth;
32969             west.updateBox(this.safeBox(b));
32970         }
32971         if(east && east.isVisible()){
32972             var b = east.getBox();
32973             var m = east.getMargins();
32974             b.height = centerH - (m.top+m.bottom);
32975             var totalWidth = (b.width + m.left + m.right);
32976             b.x = w - totalWidth + m.left;
32977             b.y = centerY + m.top;
32978             centerW -= totalWidth;
32979             east.updateBox(this.safeBox(b));
32980         }
32981         if(center){
32982             var m = center.getMargins();
32983             var centerBox = {
32984                 x: centerX + m.left,
32985                 y: centerY + m.top,
32986                 width: centerW - (m.left+m.right),
32987                 height: centerH - (m.top+m.bottom)
32988             };
32989             //if(this.hideOnLayout){
32990                 //center.el.setStyle("display", "block");
32991             //}
32992             center.updateBox(this.safeBox(centerBox));
32993         }
32994         this.el.repaint();
32995         this.fireEvent("layout", this);
32996     },
32997
32998     // private
32999     safeBox : function(box){
33000         box.width = Math.max(0, box.width);
33001         box.height = Math.max(0, box.height);
33002         return box;
33003     },
33004
33005     /**
33006      * Adds a ContentPanel (or subclass) to this layout.
33007      * @param {String} target The target region key (north, south, east, west or center).
33008      * @param {Roo.ContentPanel} panel The panel to add
33009      * @return {Roo.ContentPanel} The added panel
33010      */
33011     add : function(target, panel){
33012          
33013         target = target.toLowerCase();
33014         return this.regions[target].add(panel);
33015     },
33016
33017     /**
33018      * Remove a ContentPanel (or subclass) to this layout.
33019      * @param {String} target The target region key (north, south, east, west or center).
33020      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33021      * @return {Roo.ContentPanel} The removed panel
33022      */
33023     remove : function(target, panel){
33024         target = target.toLowerCase();
33025         return this.regions[target].remove(panel);
33026     },
33027
33028     /**
33029      * Searches all regions for a panel with the specified id
33030      * @param {String} panelId
33031      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33032      */
33033     findPanel : function(panelId){
33034         var rs = this.regions;
33035         for(var target in rs){
33036             if(typeof rs[target] != "function"){
33037                 var p = rs[target].getPanel(panelId);
33038                 if(p){
33039                     return p;
33040                 }
33041             }
33042         }
33043         return null;
33044     },
33045
33046     /**
33047      * Searches all regions for a panel with the specified id and activates (shows) it.
33048      * @param {String/ContentPanel} panelId The panels id or the panel itself
33049      * @return {Roo.ContentPanel} The shown panel or null
33050      */
33051     showPanel : function(panelId) {
33052       var rs = this.regions;
33053       for(var target in rs){
33054          var r = rs[target];
33055          if(typeof r != "function"){
33056             if(r.hasPanel(panelId)){
33057                return r.showPanel(panelId);
33058             }
33059          }
33060       }
33061       return null;
33062    },
33063
33064    /**
33065      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33066      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33067      */
33068    /*
33069     restoreState : function(provider){
33070         if(!provider){
33071             provider = Roo.state.Manager;
33072         }
33073         var sm = new Roo.LayoutStateManager();
33074         sm.init(this, provider);
33075     },
33076 */
33077  
33078  
33079     /**
33080      * Adds a xtype elements to the layout.
33081      * <pre><code>
33082
33083 layout.addxtype({
33084        xtype : 'ContentPanel',
33085        region: 'west',
33086        items: [ .... ]
33087    }
33088 );
33089
33090 layout.addxtype({
33091         xtype : 'NestedLayoutPanel',
33092         region: 'west',
33093         layout: {
33094            center: { },
33095            west: { }   
33096         },
33097         items : [ ... list of content panels or nested layout panels.. ]
33098    }
33099 );
33100 </code></pre>
33101      * @param {Object} cfg Xtype definition of item to add.
33102      */
33103     addxtype : function(cfg)
33104     {
33105         // basically accepts a pannel...
33106         // can accept a layout region..!?!?
33107         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33108         
33109         
33110         // theory?  children can only be panels??
33111         
33112         //if (!cfg.xtype.match(/Panel$/)) {
33113         //    return false;
33114         //}
33115         var ret = false;
33116         
33117         if (typeof(cfg.region) == 'undefined') {
33118             Roo.log("Failed to add Panel, region was not set");
33119             Roo.log(cfg);
33120             return false;
33121         }
33122         var region = cfg.region;
33123         delete cfg.region;
33124         
33125           
33126         var xitems = [];
33127         if (cfg.items) {
33128             xitems = cfg.items;
33129             delete cfg.items;
33130         }
33131         var nb = false;
33132         
33133         switch(cfg.xtype) 
33134         {
33135             case 'Content':  // ContentPanel (el, cfg)
33136             case 'Scroll':  // ContentPanel (el, cfg)
33137             case 'View': 
33138                 cfg.autoCreate = true;
33139                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33140                 //} else {
33141                 //    var el = this.el.createChild();
33142                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33143                 //}
33144                 
33145                 this.add(region, ret);
33146                 break;
33147             
33148             /*
33149             case 'TreePanel': // our new panel!
33150                 cfg.el = this.el.createChild();
33151                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33152                 this.add(region, ret);
33153                 break;
33154             */
33155             
33156             case 'Nest': 
33157                 // create a new Layout (which is  a Border Layout...
33158                 
33159                 var clayout = cfg.layout;
33160                 clayout.el  = this.el.createChild();
33161                 clayout.items   = clayout.items  || [];
33162                 
33163                 delete cfg.layout;
33164                 
33165                 // replace this exitems with the clayout ones..
33166                 xitems = clayout.items;
33167                  
33168                 // force background off if it's in center...
33169                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33170                     cfg.background = false;
33171                 }
33172                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33173                 
33174                 
33175                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33176                 //console.log('adding nested layout panel '  + cfg.toSource());
33177                 this.add(region, ret);
33178                 nb = {}; /// find first...
33179                 break;
33180             
33181             case 'Grid':
33182                 
33183                 // needs grid and region
33184                 
33185                 //var el = this.getRegion(region).el.createChild();
33186                 /*
33187                  *var el = this.el.createChild();
33188                 // create the grid first...
33189                 cfg.grid.container = el;
33190                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33191                 */
33192                 
33193                 if (region == 'center' && this.active ) {
33194                     cfg.background = false;
33195                 }
33196                 
33197                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33198                 
33199                 this.add(region, ret);
33200                 /*
33201                 if (cfg.background) {
33202                     // render grid on panel activation (if panel background)
33203                     ret.on('activate', function(gp) {
33204                         if (!gp.grid.rendered) {
33205                     //        gp.grid.render(el);
33206                         }
33207                     });
33208                 } else {
33209                   //  cfg.grid.render(el);
33210                 }
33211                 */
33212                 break;
33213            
33214            
33215             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33216                 // it was the old xcomponent building that caused this before.
33217                 // espeically if border is the top element in the tree.
33218                 ret = this;
33219                 break; 
33220                 
33221                     
33222                 
33223                 
33224                 
33225             default:
33226                 /*
33227                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33228                     
33229                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33230                     this.add(region, ret);
33231                 } else {
33232                 */
33233                     Roo.log(cfg);
33234                     throw "Can not add '" + cfg.xtype + "' to Border";
33235                     return null;
33236              
33237                                 
33238              
33239         }
33240         this.beginUpdate();
33241         // add children..
33242         var region = '';
33243         var abn = {};
33244         Roo.each(xitems, function(i)  {
33245             region = nb && i.region ? i.region : false;
33246             
33247             var add = ret.addxtype(i);
33248            
33249             if (region) {
33250                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33251                 if (!i.background) {
33252                     abn[region] = nb[region] ;
33253                 }
33254             }
33255             
33256         });
33257         this.endUpdate();
33258
33259         // make the last non-background panel active..
33260         //if (nb) { Roo.log(abn); }
33261         if (nb) {
33262             
33263             for(var r in abn) {
33264                 region = this.getRegion(r);
33265                 if (region) {
33266                     // tried using nb[r], but it does not work..
33267                      
33268                     region.showPanel(abn[r]);
33269                    
33270                 }
33271             }
33272         }
33273         return ret;
33274         
33275     },
33276     
33277     
33278 // private
33279     factory : function(cfg)
33280     {
33281         
33282         var validRegions = Roo.bootstrap.layout.Border.regions;
33283
33284         var target = cfg.region;
33285         cfg.mgr = this;
33286         
33287         var r = Roo.bootstrap.layout;
33288         Roo.log(target);
33289         switch(target){
33290             case "north":
33291                 return new r.North(cfg);
33292             case "south":
33293                 return new r.South(cfg);
33294             case "east":
33295                 return new r.East(cfg);
33296             case "west":
33297                 return new r.West(cfg);
33298             case "center":
33299                 return new r.Center(cfg);
33300         }
33301         throw 'Layout region "'+target+'" not supported.';
33302     }
33303     
33304     
33305 });
33306  /*
33307  * Based on:
33308  * Ext JS Library 1.1.1
33309  * Copyright(c) 2006-2007, Ext JS, LLC.
33310  *
33311  * Originally Released Under LGPL - original licence link has changed is not relivant.
33312  *
33313  * Fork - LGPL
33314  * <script type="text/javascript">
33315  */
33316  
33317 /**
33318  * @class Roo.bootstrap.layout.Basic
33319  * @extends Roo.util.Observable
33320  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33321  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33322  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33323  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33324  * @cfg {string}   region  the region that it inhabits..
33325  * @cfg {bool}   skipConfig skip config?
33326  * 
33327
33328  */
33329 Roo.bootstrap.layout.Basic = function(config){
33330     
33331     this.mgr = config.mgr;
33332     
33333     this.position = config.region;
33334     
33335     var skipConfig = config.skipConfig;
33336     
33337     this.events = {
33338         /**
33339          * @scope Roo.BasicLayoutRegion
33340          */
33341         
33342         /**
33343          * @event beforeremove
33344          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33345          * @param {Roo.LayoutRegion} this
33346          * @param {Roo.ContentPanel} panel The panel
33347          * @param {Object} e The cancel event object
33348          */
33349         "beforeremove" : true,
33350         /**
33351          * @event invalidated
33352          * Fires when the layout for this region is changed.
33353          * @param {Roo.LayoutRegion} this
33354          */
33355         "invalidated" : true,
33356         /**
33357          * @event visibilitychange
33358          * Fires when this region is shown or hidden 
33359          * @param {Roo.LayoutRegion} this
33360          * @param {Boolean} visibility true or false
33361          */
33362         "visibilitychange" : true,
33363         /**
33364          * @event paneladded
33365          * Fires when a panel is added. 
33366          * @param {Roo.LayoutRegion} this
33367          * @param {Roo.ContentPanel} panel The panel
33368          */
33369         "paneladded" : true,
33370         /**
33371          * @event panelremoved
33372          * Fires when a panel is removed. 
33373          * @param {Roo.LayoutRegion} this
33374          * @param {Roo.ContentPanel} panel The panel
33375          */
33376         "panelremoved" : true,
33377         /**
33378          * @event beforecollapse
33379          * Fires when this region before collapse.
33380          * @param {Roo.LayoutRegion} this
33381          */
33382         "beforecollapse" : true,
33383         /**
33384          * @event collapsed
33385          * Fires when this region is collapsed.
33386          * @param {Roo.LayoutRegion} this
33387          */
33388         "collapsed" : true,
33389         /**
33390          * @event expanded
33391          * Fires when this region is expanded.
33392          * @param {Roo.LayoutRegion} this
33393          */
33394         "expanded" : true,
33395         /**
33396          * @event slideshow
33397          * Fires when this region is slid into view.
33398          * @param {Roo.LayoutRegion} this
33399          */
33400         "slideshow" : true,
33401         /**
33402          * @event slidehide
33403          * Fires when this region slides out of view. 
33404          * @param {Roo.LayoutRegion} this
33405          */
33406         "slidehide" : true,
33407         /**
33408          * @event panelactivated
33409          * Fires when a panel is activated. 
33410          * @param {Roo.LayoutRegion} this
33411          * @param {Roo.ContentPanel} panel The activated panel
33412          */
33413         "panelactivated" : true,
33414         /**
33415          * @event resized
33416          * Fires when the user resizes this region. 
33417          * @param {Roo.LayoutRegion} this
33418          * @param {Number} newSize The new size (width for east/west, height for north/south)
33419          */
33420         "resized" : true
33421     };
33422     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33423     this.panels = new Roo.util.MixedCollection();
33424     this.panels.getKey = this.getPanelId.createDelegate(this);
33425     this.box = null;
33426     this.activePanel = null;
33427     // ensure listeners are added...
33428     
33429     if (config.listeners || config.events) {
33430         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33431             listeners : config.listeners || {},
33432             events : config.events || {}
33433         });
33434     }
33435     
33436     if(skipConfig !== true){
33437         this.applyConfig(config);
33438     }
33439 };
33440
33441 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33442 {
33443     getPanelId : function(p){
33444         return p.getId();
33445     },
33446     
33447     applyConfig : function(config){
33448         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33449         this.config = config;
33450         
33451     },
33452     
33453     /**
33454      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33455      * the width, for horizontal (north, south) the height.
33456      * @param {Number} newSize The new width or height
33457      */
33458     resizeTo : function(newSize){
33459         var el = this.el ? this.el :
33460                  (this.activePanel ? this.activePanel.getEl() : null);
33461         if(el){
33462             switch(this.position){
33463                 case "east":
33464                 case "west":
33465                     el.setWidth(newSize);
33466                     this.fireEvent("resized", this, newSize);
33467                 break;
33468                 case "north":
33469                 case "south":
33470                     el.setHeight(newSize);
33471                     this.fireEvent("resized", this, newSize);
33472                 break;                
33473             }
33474         }
33475     },
33476     
33477     getBox : function(){
33478         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33479     },
33480     
33481     getMargins : function(){
33482         return this.margins;
33483     },
33484     
33485     updateBox : function(box){
33486         this.box = box;
33487         var el = this.activePanel.getEl();
33488         el.dom.style.left = box.x + "px";
33489         el.dom.style.top = box.y + "px";
33490         this.activePanel.setSize(box.width, box.height);
33491     },
33492     
33493     /**
33494      * Returns the container element for this region.
33495      * @return {Roo.Element}
33496      */
33497     getEl : function(){
33498         return this.activePanel;
33499     },
33500     
33501     /**
33502      * Returns true if this region is currently visible.
33503      * @return {Boolean}
33504      */
33505     isVisible : function(){
33506         return this.activePanel ? true : false;
33507     },
33508     
33509     setActivePanel : function(panel){
33510         panel = this.getPanel(panel);
33511         if(this.activePanel && this.activePanel != panel){
33512             this.activePanel.setActiveState(false);
33513             this.activePanel.getEl().setLeftTop(-10000,-10000);
33514         }
33515         this.activePanel = panel;
33516         panel.setActiveState(true);
33517         if(this.box){
33518             panel.setSize(this.box.width, this.box.height);
33519         }
33520         this.fireEvent("panelactivated", this, panel);
33521         this.fireEvent("invalidated");
33522     },
33523     
33524     /**
33525      * Show the specified panel.
33526      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33527      * @return {Roo.ContentPanel} The shown panel or null
33528      */
33529     showPanel : function(panel){
33530         panel = this.getPanel(panel);
33531         if(panel){
33532             this.setActivePanel(panel);
33533         }
33534         return panel;
33535     },
33536     
33537     /**
33538      * Get the active panel for this region.
33539      * @return {Roo.ContentPanel} The active panel or null
33540      */
33541     getActivePanel : function(){
33542         return this.activePanel;
33543     },
33544     
33545     /**
33546      * Add the passed ContentPanel(s)
33547      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33548      * @return {Roo.ContentPanel} The panel added (if only one was added)
33549      */
33550     add : function(panel){
33551         if(arguments.length > 1){
33552             for(var i = 0, len = arguments.length; i < len; i++) {
33553                 this.add(arguments[i]);
33554             }
33555             return null;
33556         }
33557         if(this.hasPanel(panel)){
33558             this.showPanel(panel);
33559             return panel;
33560         }
33561         var el = panel.getEl();
33562         if(el.dom.parentNode != this.mgr.el.dom){
33563             this.mgr.el.dom.appendChild(el.dom);
33564         }
33565         if(panel.setRegion){
33566             panel.setRegion(this);
33567         }
33568         this.panels.add(panel);
33569         el.setStyle("position", "absolute");
33570         if(!panel.background){
33571             this.setActivePanel(panel);
33572             if(this.config.initialSize && this.panels.getCount()==1){
33573                 this.resizeTo(this.config.initialSize);
33574             }
33575         }
33576         this.fireEvent("paneladded", this, panel);
33577         return panel;
33578     },
33579     
33580     /**
33581      * Returns true if the panel is in this region.
33582      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33583      * @return {Boolean}
33584      */
33585     hasPanel : function(panel){
33586         if(typeof panel == "object"){ // must be panel obj
33587             panel = panel.getId();
33588         }
33589         return this.getPanel(panel) ? true : false;
33590     },
33591     
33592     /**
33593      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33594      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33595      * @param {Boolean} preservePanel Overrides the config preservePanel option
33596      * @return {Roo.ContentPanel} The panel that was removed
33597      */
33598     remove : function(panel, preservePanel){
33599         panel = this.getPanel(panel);
33600         if(!panel){
33601             return null;
33602         }
33603         var e = {};
33604         this.fireEvent("beforeremove", this, panel, e);
33605         if(e.cancel === true){
33606             return null;
33607         }
33608         var panelId = panel.getId();
33609         this.panels.removeKey(panelId);
33610         return panel;
33611     },
33612     
33613     /**
33614      * Returns the panel specified or null if it's not in this region.
33615      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33616      * @return {Roo.ContentPanel}
33617      */
33618     getPanel : function(id){
33619         if(typeof id == "object"){ // must be panel obj
33620             return id;
33621         }
33622         return this.panels.get(id);
33623     },
33624     
33625     /**
33626      * Returns this regions position (north/south/east/west/center).
33627      * @return {String} 
33628      */
33629     getPosition: function(){
33630         return this.position;    
33631     }
33632 });/*
33633  * Based on:
33634  * Ext JS Library 1.1.1
33635  * Copyright(c) 2006-2007, Ext JS, LLC.
33636  *
33637  * Originally Released Under LGPL - original licence link has changed is not relivant.
33638  *
33639  * Fork - LGPL
33640  * <script type="text/javascript">
33641  */
33642  
33643 /**
33644  * @class Roo.bootstrap.layout.Region
33645  * @extends Roo.bootstrap.layout.Basic
33646  * This class represents a region in a layout manager.
33647  
33648  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33649  * @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})
33650  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33651  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33652  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33653  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33654  * @cfg {String}    title           The title for the region (overrides panel titles)
33655  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33656  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33657  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33658  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33659  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33660  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33661  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33662  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33663  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33664  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33665
33666  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33667  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33668  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33669  * @cfg {Number}    width           For East/West panels
33670  * @cfg {Number}    height          For North/South panels
33671  * @cfg {Boolean}   split           To show the splitter
33672  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33673  * 
33674  * @cfg {string}   cls             Extra CSS classes to add to region
33675  * 
33676  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33677  * @cfg {string}   region  the region that it inhabits..
33678  *
33679
33680  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33681  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33682
33683  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33684  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33685  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33686  */
33687 Roo.bootstrap.layout.Region = function(config)
33688 {
33689     this.applyConfig(config);
33690
33691     var mgr = config.mgr;
33692     var pos = config.region;
33693     config.skipConfig = true;
33694     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33695     
33696     if (mgr.el) {
33697         this.onRender(mgr.el);   
33698     }
33699      
33700     this.visible = true;
33701     this.collapsed = false;
33702     this.unrendered_panels = [];
33703 };
33704
33705 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33706
33707     position: '', // set by wrapper (eg. north/south etc..)
33708     unrendered_panels : null,  // unrendered panels.
33709     createBody : function(){
33710         /** This region's body element 
33711         * @type Roo.Element */
33712         this.bodyEl = this.el.createChild({
33713                 tag: "div",
33714                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33715         });
33716     },
33717
33718     onRender: function(ctr, pos)
33719     {
33720         var dh = Roo.DomHelper;
33721         /** This region's container element 
33722         * @type Roo.Element */
33723         this.el = dh.append(ctr.dom, {
33724                 tag: "div",
33725                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33726             }, true);
33727         /** This region's title element 
33728         * @type Roo.Element */
33729     
33730         this.titleEl = dh.append(this.el.dom,
33731             {
33732                     tag: "div",
33733                     unselectable: "on",
33734                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33735                     children:[
33736                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33737                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33738                     ]}, true);
33739         
33740         this.titleEl.enableDisplayMode();
33741         /** This region's title text element 
33742         * @type HTMLElement */
33743         this.titleTextEl = this.titleEl.dom.firstChild;
33744         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33745         /*
33746         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33747         this.closeBtn.enableDisplayMode();
33748         this.closeBtn.on("click", this.closeClicked, this);
33749         this.closeBtn.hide();
33750     */
33751         this.createBody(this.config);
33752         if(this.config.hideWhenEmpty){
33753             this.hide();
33754             this.on("paneladded", this.validateVisibility, this);
33755             this.on("panelremoved", this.validateVisibility, this);
33756         }
33757         if(this.autoScroll){
33758             this.bodyEl.setStyle("overflow", "auto");
33759         }else{
33760             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33761         }
33762         //if(c.titlebar !== false){
33763             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33764                 this.titleEl.hide();
33765             }else{
33766                 this.titleEl.show();
33767                 if(this.config.title){
33768                     this.titleTextEl.innerHTML = this.config.title;
33769                 }
33770             }
33771         //}
33772         if(this.config.collapsed){
33773             this.collapse(true);
33774         }
33775         if(this.config.hidden){
33776             this.hide();
33777         }
33778         
33779         if (this.unrendered_panels && this.unrendered_panels.length) {
33780             for (var i =0;i< this.unrendered_panels.length; i++) {
33781                 this.add(this.unrendered_panels[i]);
33782             }
33783             this.unrendered_panels = null;
33784             
33785         }
33786         
33787     },
33788     
33789     applyConfig : function(c)
33790     {
33791         /*
33792          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33793             var dh = Roo.DomHelper;
33794             if(c.titlebar !== false){
33795                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33796                 this.collapseBtn.on("click", this.collapse, this);
33797                 this.collapseBtn.enableDisplayMode();
33798                 /*
33799                 if(c.showPin === true || this.showPin){
33800                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33801                     this.stickBtn.enableDisplayMode();
33802                     this.stickBtn.on("click", this.expand, this);
33803                     this.stickBtn.hide();
33804                 }
33805                 
33806             }
33807             */
33808             /** This region's collapsed element
33809             * @type Roo.Element */
33810             /*
33811              *
33812             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33813                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33814             ]}, true);
33815             
33816             if(c.floatable !== false){
33817                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33818                this.collapsedEl.on("click", this.collapseClick, this);
33819             }
33820
33821             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33822                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33823                    id: "message", unselectable: "on", style:{"float":"left"}});
33824                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33825              }
33826             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33827             this.expandBtn.on("click", this.expand, this);
33828             
33829         }
33830         
33831         if(this.collapseBtn){
33832             this.collapseBtn.setVisible(c.collapsible == true);
33833         }
33834         
33835         this.cmargins = c.cmargins || this.cmargins ||
33836                          (this.position == "west" || this.position == "east" ?
33837                              {top: 0, left: 2, right:2, bottom: 0} :
33838                              {top: 2, left: 0, right:0, bottom: 2});
33839         */
33840         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33841         
33842         
33843         this.bottomTabs = c.tabPosition != "top";
33844         
33845         this.autoScroll = c.autoScroll || false;
33846         
33847         
33848        
33849         
33850         this.duration = c.duration || .30;
33851         this.slideDuration = c.slideDuration || .45;
33852         this.config = c;
33853        
33854     },
33855     /**
33856      * Returns true if this region is currently visible.
33857      * @return {Boolean}
33858      */
33859     isVisible : function(){
33860         return this.visible;
33861     },
33862
33863     /**
33864      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33865      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33866      */
33867     //setCollapsedTitle : function(title){
33868     //    title = title || "&#160;";
33869      //   if(this.collapsedTitleTextEl){
33870       //      this.collapsedTitleTextEl.innerHTML = title;
33871        // }
33872     //},
33873
33874     getBox : function(){
33875         var b;
33876       //  if(!this.collapsed){
33877             b = this.el.getBox(false, true);
33878        // }else{
33879           //  b = this.collapsedEl.getBox(false, true);
33880         //}
33881         return b;
33882     },
33883
33884     getMargins : function(){
33885         return this.margins;
33886         //return this.collapsed ? this.cmargins : this.margins;
33887     },
33888 /*
33889     highlight : function(){
33890         this.el.addClass("x-layout-panel-dragover");
33891     },
33892
33893     unhighlight : function(){
33894         this.el.removeClass("x-layout-panel-dragover");
33895     },
33896 */
33897     updateBox : function(box)
33898     {
33899         if (!this.bodyEl) {
33900             return; // not rendered yet..
33901         }
33902         
33903         this.box = box;
33904         if(!this.collapsed){
33905             this.el.dom.style.left = box.x + "px";
33906             this.el.dom.style.top = box.y + "px";
33907             this.updateBody(box.width, box.height);
33908         }else{
33909             this.collapsedEl.dom.style.left = box.x + "px";
33910             this.collapsedEl.dom.style.top = box.y + "px";
33911             this.collapsedEl.setSize(box.width, box.height);
33912         }
33913         if(this.tabs){
33914             this.tabs.autoSizeTabs();
33915         }
33916     },
33917
33918     updateBody : function(w, h)
33919     {
33920         if(w !== null){
33921             this.el.setWidth(w);
33922             w -= this.el.getBorderWidth("rl");
33923             if(this.config.adjustments){
33924                 w += this.config.adjustments[0];
33925             }
33926         }
33927         if(h !== null && h > 0){
33928             this.el.setHeight(h);
33929             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33930             h -= this.el.getBorderWidth("tb");
33931             if(this.config.adjustments){
33932                 h += this.config.adjustments[1];
33933             }
33934             this.bodyEl.setHeight(h);
33935             if(this.tabs){
33936                 h = this.tabs.syncHeight(h);
33937             }
33938         }
33939         if(this.panelSize){
33940             w = w !== null ? w : this.panelSize.width;
33941             h = h !== null ? h : this.panelSize.height;
33942         }
33943         if(this.activePanel){
33944             var el = this.activePanel.getEl();
33945             w = w !== null ? w : el.getWidth();
33946             h = h !== null ? h : el.getHeight();
33947             this.panelSize = {width: w, height: h};
33948             this.activePanel.setSize(w, h);
33949         }
33950         if(Roo.isIE && this.tabs){
33951             this.tabs.el.repaint();
33952         }
33953     },
33954
33955     /**
33956      * Returns the container element for this region.
33957      * @return {Roo.Element}
33958      */
33959     getEl : function(){
33960         return this.el;
33961     },
33962
33963     /**
33964      * Hides this region.
33965      */
33966     hide : function(){
33967         //if(!this.collapsed){
33968             this.el.dom.style.left = "-2000px";
33969             this.el.hide();
33970         //}else{
33971          //   this.collapsedEl.dom.style.left = "-2000px";
33972          //   this.collapsedEl.hide();
33973        // }
33974         this.visible = false;
33975         this.fireEvent("visibilitychange", this, false);
33976     },
33977
33978     /**
33979      * Shows this region if it was previously hidden.
33980      */
33981     show : function(){
33982         //if(!this.collapsed){
33983             this.el.show();
33984         //}else{
33985         //    this.collapsedEl.show();
33986        // }
33987         this.visible = true;
33988         this.fireEvent("visibilitychange", this, true);
33989     },
33990 /*
33991     closeClicked : function(){
33992         if(this.activePanel){
33993             this.remove(this.activePanel);
33994         }
33995     },
33996
33997     collapseClick : function(e){
33998         if(this.isSlid){
33999            e.stopPropagation();
34000            this.slideIn();
34001         }else{
34002            e.stopPropagation();
34003            this.slideOut();
34004         }
34005     },
34006 */
34007     /**
34008      * Collapses this region.
34009      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34010      */
34011     /*
34012     collapse : function(skipAnim, skipCheck = false){
34013         if(this.collapsed) {
34014             return;
34015         }
34016         
34017         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34018             
34019             this.collapsed = true;
34020             if(this.split){
34021                 this.split.el.hide();
34022             }
34023             if(this.config.animate && skipAnim !== true){
34024                 this.fireEvent("invalidated", this);
34025                 this.animateCollapse();
34026             }else{
34027                 this.el.setLocation(-20000,-20000);
34028                 this.el.hide();
34029                 this.collapsedEl.show();
34030                 this.fireEvent("collapsed", this);
34031                 this.fireEvent("invalidated", this);
34032             }
34033         }
34034         
34035     },
34036 */
34037     animateCollapse : function(){
34038         // overridden
34039     },
34040
34041     /**
34042      * Expands this region if it was previously collapsed.
34043      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34044      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34045      */
34046     /*
34047     expand : function(e, skipAnim){
34048         if(e) {
34049             e.stopPropagation();
34050         }
34051         if(!this.collapsed || this.el.hasActiveFx()) {
34052             return;
34053         }
34054         if(this.isSlid){
34055             this.afterSlideIn();
34056             skipAnim = true;
34057         }
34058         this.collapsed = false;
34059         if(this.config.animate && skipAnim !== true){
34060             this.animateExpand();
34061         }else{
34062             this.el.show();
34063             if(this.split){
34064                 this.split.el.show();
34065             }
34066             this.collapsedEl.setLocation(-2000,-2000);
34067             this.collapsedEl.hide();
34068             this.fireEvent("invalidated", this);
34069             this.fireEvent("expanded", this);
34070         }
34071     },
34072 */
34073     animateExpand : function(){
34074         // overridden
34075     },
34076
34077     initTabs : function()
34078     {
34079         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34080         
34081         var ts = new Roo.bootstrap.panel.Tabs({
34082                 el: this.bodyEl.dom,
34083                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34084                 disableTooltips: this.config.disableTabTips,
34085                 toolbar : this.config.toolbar
34086             });
34087         
34088         if(this.config.hideTabs){
34089             ts.stripWrap.setDisplayed(false);
34090         }
34091         this.tabs = ts;
34092         ts.resizeTabs = this.config.resizeTabs === true;
34093         ts.minTabWidth = this.config.minTabWidth || 40;
34094         ts.maxTabWidth = this.config.maxTabWidth || 250;
34095         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34096         ts.monitorResize = false;
34097         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34098         ts.bodyEl.addClass('roo-layout-tabs-body');
34099         this.panels.each(this.initPanelAsTab, this);
34100     },
34101
34102     initPanelAsTab : function(panel){
34103         var ti = this.tabs.addTab(
34104             panel.getEl().id,
34105             panel.getTitle(),
34106             null,
34107             this.config.closeOnTab && panel.isClosable(),
34108             panel.tpl
34109         );
34110         if(panel.tabTip !== undefined){
34111             ti.setTooltip(panel.tabTip);
34112         }
34113         ti.on("activate", function(){
34114               this.setActivePanel(panel);
34115         }, this);
34116         
34117         if(this.config.closeOnTab){
34118             ti.on("beforeclose", function(t, e){
34119                 e.cancel = true;
34120                 this.remove(panel);
34121             }, this);
34122         }
34123         
34124         panel.tabItem = ti;
34125         
34126         return ti;
34127     },
34128
34129     updatePanelTitle : function(panel, title)
34130     {
34131         if(this.activePanel == panel){
34132             this.updateTitle(title);
34133         }
34134         if(this.tabs){
34135             var ti = this.tabs.getTab(panel.getEl().id);
34136             ti.setText(title);
34137             if(panel.tabTip !== undefined){
34138                 ti.setTooltip(panel.tabTip);
34139             }
34140         }
34141     },
34142
34143     updateTitle : function(title){
34144         if(this.titleTextEl && !this.config.title){
34145             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34146         }
34147     },
34148
34149     setActivePanel : function(panel)
34150     {
34151         panel = this.getPanel(panel);
34152         if(this.activePanel && this.activePanel != panel){
34153             this.activePanel.setActiveState(false);
34154         }
34155         this.activePanel = panel;
34156         panel.setActiveState(true);
34157         if(this.panelSize){
34158             panel.setSize(this.panelSize.width, this.panelSize.height);
34159         }
34160         if(this.closeBtn){
34161             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34162         }
34163         this.updateTitle(panel.getTitle());
34164         if(this.tabs){
34165             this.fireEvent("invalidated", this);
34166         }
34167         this.fireEvent("panelactivated", this, panel);
34168     },
34169
34170     /**
34171      * Shows the specified panel.
34172      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34173      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34174      */
34175     showPanel : function(panel)
34176     {
34177         panel = this.getPanel(panel);
34178         if(panel){
34179             if(this.tabs){
34180                 var tab = this.tabs.getTab(panel.getEl().id);
34181                 if(tab.isHidden()){
34182                     this.tabs.unhideTab(tab.id);
34183                 }
34184                 tab.activate();
34185             }else{
34186                 this.setActivePanel(panel);
34187             }
34188         }
34189         return panel;
34190     },
34191
34192     /**
34193      * Get the active panel for this region.
34194      * @return {Roo.ContentPanel} The active panel or null
34195      */
34196     getActivePanel : function(){
34197         return this.activePanel;
34198     },
34199
34200     validateVisibility : function(){
34201         if(this.panels.getCount() < 1){
34202             this.updateTitle("&#160;");
34203             this.closeBtn.hide();
34204             this.hide();
34205         }else{
34206             if(!this.isVisible()){
34207                 this.show();
34208             }
34209         }
34210     },
34211
34212     /**
34213      * Adds the passed ContentPanel(s) to this region.
34214      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34215      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34216      */
34217     add : function(panel)
34218     {
34219         if(arguments.length > 1){
34220             for(var i = 0, len = arguments.length; i < len; i++) {
34221                 this.add(arguments[i]);
34222             }
34223             return null;
34224         }
34225         
34226         // if we have not been rendered yet, then we can not really do much of this..
34227         if (!this.bodyEl) {
34228             this.unrendered_panels.push(panel);
34229             return panel;
34230         }
34231         
34232         
34233         
34234         
34235         if(this.hasPanel(panel)){
34236             this.showPanel(panel);
34237             return panel;
34238         }
34239         panel.setRegion(this);
34240         this.panels.add(panel);
34241        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34242             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34243             // and hide them... ???
34244             this.bodyEl.dom.appendChild(panel.getEl().dom);
34245             if(panel.background !== true){
34246                 this.setActivePanel(panel);
34247             }
34248             this.fireEvent("paneladded", this, panel);
34249             return panel;
34250         }
34251         */
34252         if(!this.tabs){
34253             this.initTabs();
34254         }else{
34255             this.initPanelAsTab(panel);
34256         }
34257         
34258         
34259         if(panel.background !== true){
34260             this.tabs.activate(panel.getEl().id);
34261         }
34262         this.fireEvent("paneladded", this, panel);
34263         return panel;
34264     },
34265
34266     /**
34267      * Hides the tab for the specified panel.
34268      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34269      */
34270     hidePanel : function(panel){
34271         if(this.tabs && (panel = this.getPanel(panel))){
34272             this.tabs.hideTab(panel.getEl().id);
34273         }
34274     },
34275
34276     /**
34277      * Unhides the tab for a previously hidden panel.
34278      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34279      */
34280     unhidePanel : function(panel){
34281         if(this.tabs && (panel = this.getPanel(panel))){
34282             this.tabs.unhideTab(panel.getEl().id);
34283         }
34284     },
34285
34286     clearPanels : function(){
34287         while(this.panels.getCount() > 0){
34288              this.remove(this.panels.first());
34289         }
34290     },
34291
34292     /**
34293      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34294      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34295      * @param {Boolean} preservePanel Overrides the config preservePanel option
34296      * @return {Roo.ContentPanel} The panel that was removed
34297      */
34298     remove : function(panel, preservePanel)
34299     {
34300         panel = this.getPanel(panel);
34301         if(!panel){
34302             return null;
34303         }
34304         var e = {};
34305         this.fireEvent("beforeremove", this, panel, e);
34306         if(e.cancel === true){
34307             return null;
34308         }
34309         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34310         var panelId = panel.getId();
34311         this.panels.removeKey(panelId);
34312         if(preservePanel){
34313             document.body.appendChild(panel.getEl().dom);
34314         }
34315         if(this.tabs){
34316             this.tabs.removeTab(panel.getEl().id);
34317         }else if (!preservePanel){
34318             this.bodyEl.dom.removeChild(panel.getEl().dom);
34319         }
34320         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34321             var p = this.panels.first();
34322             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34323             tempEl.appendChild(p.getEl().dom);
34324             this.bodyEl.update("");
34325             this.bodyEl.dom.appendChild(p.getEl().dom);
34326             tempEl = null;
34327             this.updateTitle(p.getTitle());
34328             this.tabs = null;
34329             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34330             this.setActivePanel(p);
34331         }
34332         panel.setRegion(null);
34333         if(this.activePanel == panel){
34334             this.activePanel = null;
34335         }
34336         if(this.config.autoDestroy !== false && preservePanel !== true){
34337             try{panel.destroy();}catch(e){}
34338         }
34339         this.fireEvent("panelremoved", this, panel);
34340         return panel;
34341     },
34342
34343     /**
34344      * Returns the TabPanel component used by this region
34345      * @return {Roo.TabPanel}
34346      */
34347     getTabs : function(){
34348         return this.tabs;
34349     },
34350
34351     createTool : function(parentEl, className){
34352         var btn = Roo.DomHelper.append(parentEl, {
34353             tag: "div",
34354             cls: "x-layout-tools-button",
34355             children: [ {
34356                 tag: "div",
34357                 cls: "roo-layout-tools-button-inner " + className,
34358                 html: "&#160;"
34359             }]
34360         }, true);
34361         btn.addClassOnOver("roo-layout-tools-button-over");
34362         return btn;
34363     }
34364 });/*
34365  * Based on:
34366  * Ext JS Library 1.1.1
34367  * Copyright(c) 2006-2007, Ext JS, LLC.
34368  *
34369  * Originally Released Under LGPL - original licence link has changed is not relivant.
34370  *
34371  * Fork - LGPL
34372  * <script type="text/javascript">
34373  */
34374  
34375
34376
34377 /**
34378  * @class Roo.SplitLayoutRegion
34379  * @extends Roo.LayoutRegion
34380  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34381  */
34382 Roo.bootstrap.layout.Split = function(config){
34383     this.cursor = config.cursor;
34384     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34385 };
34386
34387 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34388 {
34389     splitTip : "Drag to resize.",
34390     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34391     useSplitTips : false,
34392
34393     applyConfig : function(config){
34394         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34395     },
34396     
34397     onRender : function(ctr,pos) {
34398         
34399         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34400         if(!this.config.split){
34401             return;
34402         }
34403         if(!this.split){
34404             
34405             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34406                             tag: "div",
34407                             id: this.el.id + "-split",
34408                             cls: "roo-layout-split roo-layout-split-"+this.position,
34409                             html: "&#160;"
34410             });
34411             /** The SplitBar for this region 
34412             * @type Roo.SplitBar */
34413             // does not exist yet...
34414             Roo.log([this.position, this.orientation]);
34415             
34416             this.split = new Roo.bootstrap.SplitBar({
34417                 dragElement : splitEl,
34418                 resizingElement: this.el,
34419                 orientation : this.orientation
34420             });
34421             
34422             this.split.on("moved", this.onSplitMove, this);
34423             this.split.useShim = this.config.useShim === true;
34424             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34425             if(this.useSplitTips){
34426                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34427             }
34428             //if(config.collapsible){
34429             //    this.split.el.on("dblclick", this.collapse,  this);
34430             //}
34431         }
34432         if(typeof this.config.minSize != "undefined"){
34433             this.split.minSize = this.config.minSize;
34434         }
34435         if(typeof this.config.maxSize != "undefined"){
34436             this.split.maxSize = this.config.maxSize;
34437         }
34438         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34439             this.hideSplitter();
34440         }
34441         
34442     },
34443
34444     getHMaxSize : function(){
34445          var cmax = this.config.maxSize || 10000;
34446          var center = this.mgr.getRegion("center");
34447          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34448     },
34449
34450     getVMaxSize : function(){
34451          var cmax = this.config.maxSize || 10000;
34452          var center = this.mgr.getRegion("center");
34453          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34454     },
34455
34456     onSplitMove : function(split, newSize){
34457         this.fireEvent("resized", this, newSize);
34458     },
34459     
34460     /** 
34461      * Returns the {@link Roo.SplitBar} for this region.
34462      * @return {Roo.SplitBar}
34463      */
34464     getSplitBar : function(){
34465         return this.split;
34466     },
34467     
34468     hide : function(){
34469         this.hideSplitter();
34470         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34471     },
34472
34473     hideSplitter : function(){
34474         if(this.split){
34475             this.split.el.setLocation(-2000,-2000);
34476             this.split.el.hide();
34477         }
34478     },
34479
34480     show : function(){
34481         if(this.split){
34482             this.split.el.show();
34483         }
34484         Roo.bootstrap.layout.Split.superclass.show.call(this);
34485     },
34486     
34487     beforeSlide: function(){
34488         if(Roo.isGecko){// firefox overflow auto bug workaround
34489             this.bodyEl.clip();
34490             if(this.tabs) {
34491                 this.tabs.bodyEl.clip();
34492             }
34493             if(this.activePanel){
34494                 this.activePanel.getEl().clip();
34495                 
34496                 if(this.activePanel.beforeSlide){
34497                     this.activePanel.beforeSlide();
34498                 }
34499             }
34500         }
34501     },
34502     
34503     afterSlide : function(){
34504         if(Roo.isGecko){// firefox overflow auto bug workaround
34505             this.bodyEl.unclip();
34506             if(this.tabs) {
34507                 this.tabs.bodyEl.unclip();
34508             }
34509             if(this.activePanel){
34510                 this.activePanel.getEl().unclip();
34511                 if(this.activePanel.afterSlide){
34512                     this.activePanel.afterSlide();
34513                 }
34514             }
34515         }
34516     },
34517
34518     initAutoHide : function(){
34519         if(this.autoHide !== false){
34520             if(!this.autoHideHd){
34521                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34522                 this.autoHideHd = {
34523                     "mouseout": function(e){
34524                         if(!e.within(this.el, true)){
34525                             st.delay(500);
34526                         }
34527                     },
34528                     "mouseover" : function(e){
34529                         st.cancel();
34530                     },
34531                     scope : this
34532                 };
34533             }
34534             this.el.on(this.autoHideHd);
34535         }
34536     },
34537
34538     clearAutoHide : function(){
34539         if(this.autoHide !== false){
34540             this.el.un("mouseout", this.autoHideHd.mouseout);
34541             this.el.un("mouseover", this.autoHideHd.mouseover);
34542         }
34543     },
34544
34545     clearMonitor : function(){
34546         Roo.get(document).un("click", this.slideInIf, this);
34547     },
34548
34549     // these names are backwards but not changed for compat
34550     slideOut : function(){
34551         if(this.isSlid || this.el.hasActiveFx()){
34552             return;
34553         }
34554         this.isSlid = true;
34555         if(this.collapseBtn){
34556             this.collapseBtn.hide();
34557         }
34558         this.closeBtnState = this.closeBtn.getStyle('display');
34559         this.closeBtn.hide();
34560         if(this.stickBtn){
34561             this.stickBtn.show();
34562         }
34563         this.el.show();
34564         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34565         this.beforeSlide();
34566         this.el.setStyle("z-index", 10001);
34567         this.el.slideIn(this.getSlideAnchor(), {
34568             callback: function(){
34569                 this.afterSlide();
34570                 this.initAutoHide();
34571                 Roo.get(document).on("click", this.slideInIf, this);
34572                 this.fireEvent("slideshow", this);
34573             },
34574             scope: this,
34575             block: true
34576         });
34577     },
34578
34579     afterSlideIn : function(){
34580         this.clearAutoHide();
34581         this.isSlid = false;
34582         this.clearMonitor();
34583         this.el.setStyle("z-index", "");
34584         if(this.collapseBtn){
34585             this.collapseBtn.show();
34586         }
34587         this.closeBtn.setStyle('display', this.closeBtnState);
34588         if(this.stickBtn){
34589             this.stickBtn.hide();
34590         }
34591         this.fireEvent("slidehide", this);
34592     },
34593
34594     slideIn : function(cb){
34595         if(!this.isSlid || this.el.hasActiveFx()){
34596             Roo.callback(cb);
34597             return;
34598         }
34599         this.isSlid = false;
34600         this.beforeSlide();
34601         this.el.slideOut(this.getSlideAnchor(), {
34602             callback: function(){
34603                 this.el.setLeftTop(-10000, -10000);
34604                 this.afterSlide();
34605                 this.afterSlideIn();
34606                 Roo.callback(cb);
34607             },
34608             scope: this,
34609             block: true
34610         });
34611     },
34612     
34613     slideInIf : function(e){
34614         if(!e.within(this.el)){
34615             this.slideIn();
34616         }
34617     },
34618
34619     animateCollapse : function(){
34620         this.beforeSlide();
34621         this.el.setStyle("z-index", 20000);
34622         var anchor = this.getSlideAnchor();
34623         this.el.slideOut(anchor, {
34624             callback : function(){
34625                 this.el.setStyle("z-index", "");
34626                 this.collapsedEl.slideIn(anchor, {duration:.3});
34627                 this.afterSlide();
34628                 this.el.setLocation(-10000,-10000);
34629                 this.el.hide();
34630                 this.fireEvent("collapsed", this);
34631             },
34632             scope: this,
34633             block: true
34634         });
34635     },
34636
34637     animateExpand : function(){
34638         this.beforeSlide();
34639         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34640         this.el.setStyle("z-index", 20000);
34641         this.collapsedEl.hide({
34642             duration:.1
34643         });
34644         this.el.slideIn(this.getSlideAnchor(), {
34645             callback : function(){
34646                 this.el.setStyle("z-index", "");
34647                 this.afterSlide();
34648                 if(this.split){
34649                     this.split.el.show();
34650                 }
34651                 this.fireEvent("invalidated", this);
34652                 this.fireEvent("expanded", this);
34653             },
34654             scope: this,
34655             block: true
34656         });
34657     },
34658
34659     anchors : {
34660         "west" : "left",
34661         "east" : "right",
34662         "north" : "top",
34663         "south" : "bottom"
34664     },
34665
34666     sanchors : {
34667         "west" : "l",
34668         "east" : "r",
34669         "north" : "t",
34670         "south" : "b"
34671     },
34672
34673     canchors : {
34674         "west" : "tl-tr",
34675         "east" : "tr-tl",
34676         "north" : "tl-bl",
34677         "south" : "bl-tl"
34678     },
34679
34680     getAnchor : function(){
34681         return this.anchors[this.position];
34682     },
34683
34684     getCollapseAnchor : function(){
34685         return this.canchors[this.position];
34686     },
34687
34688     getSlideAnchor : function(){
34689         return this.sanchors[this.position];
34690     },
34691
34692     getAlignAdj : function(){
34693         var cm = this.cmargins;
34694         switch(this.position){
34695             case "west":
34696                 return [0, 0];
34697             break;
34698             case "east":
34699                 return [0, 0];
34700             break;
34701             case "north":
34702                 return [0, 0];
34703             break;
34704             case "south":
34705                 return [0, 0];
34706             break;
34707         }
34708     },
34709
34710     getExpandAdj : function(){
34711         var c = this.collapsedEl, cm = this.cmargins;
34712         switch(this.position){
34713             case "west":
34714                 return [-(cm.right+c.getWidth()+cm.left), 0];
34715             break;
34716             case "east":
34717                 return [cm.right+c.getWidth()+cm.left, 0];
34718             break;
34719             case "north":
34720                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34721             break;
34722             case "south":
34723                 return [0, cm.top+cm.bottom+c.getHeight()];
34724             break;
34725         }
34726     }
34727 });/*
34728  * Based on:
34729  * Ext JS Library 1.1.1
34730  * Copyright(c) 2006-2007, Ext JS, LLC.
34731  *
34732  * Originally Released Under LGPL - original licence link has changed is not relivant.
34733  *
34734  * Fork - LGPL
34735  * <script type="text/javascript">
34736  */
34737 /*
34738  * These classes are private internal classes
34739  */
34740 Roo.bootstrap.layout.Center = function(config){
34741     config.region = "center";
34742     Roo.bootstrap.layout.Region.call(this, config);
34743     this.visible = true;
34744     this.minWidth = config.minWidth || 20;
34745     this.minHeight = config.minHeight || 20;
34746 };
34747
34748 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34749     hide : function(){
34750         // center panel can't be hidden
34751     },
34752     
34753     show : function(){
34754         // center panel can't be hidden
34755     },
34756     
34757     getMinWidth: function(){
34758         return this.minWidth;
34759     },
34760     
34761     getMinHeight: function(){
34762         return this.minHeight;
34763     }
34764 });
34765
34766
34767
34768
34769  
34770
34771
34772
34773
34774
34775 Roo.bootstrap.layout.North = function(config)
34776 {
34777     config.region = 'north';
34778     config.cursor = 'n-resize';
34779     
34780     Roo.bootstrap.layout.Split.call(this, config);
34781     
34782     
34783     if(this.split){
34784         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34785         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34786         this.split.el.addClass("roo-layout-split-v");
34787     }
34788     var size = config.initialSize || config.height;
34789     if(typeof size != "undefined"){
34790         this.el.setHeight(size);
34791     }
34792 };
34793 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34794 {
34795     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34796     
34797     
34798     
34799     getBox : function(){
34800         if(this.collapsed){
34801             return this.collapsedEl.getBox();
34802         }
34803         var box = this.el.getBox();
34804         if(this.split){
34805             box.height += this.split.el.getHeight();
34806         }
34807         return box;
34808     },
34809     
34810     updateBox : function(box){
34811         if(this.split && !this.collapsed){
34812             box.height -= this.split.el.getHeight();
34813             this.split.el.setLeft(box.x);
34814             this.split.el.setTop(box.y+box.height);
34815             this.split.el.setWidth(box.width);
34816         }
34817         if(this.collapsed){
34818             this.updateBody(box.width, null);
34819         }
34820         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34821     }
34822 });
34823
34824
34825
34826
34827
34828 Roo.bootstrap.layout.South = function(config){
34829     config.region = 'south';
34830     config.cursor = 's-resize';
34831     Roo.bootstrap.layout.Split.call(this, config);
34832     if(this.split){
34833         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34834         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34835         this.split.el.addClass("roo-layout-split-v");
34836     }
34837     var size = config.initialSize || config.height;
34838     if(typeof size != "undefined"){
34839         this.el.setHeight(size);
34840     }
34841 };
34842
34843 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34844     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34845     getBox : function(){
34846         if(this.collapsed){
34847             return this.collapsedEl.getBox();
34848         }
34849         var box = this.el.getBox();
34850         if(this.split){
34851             var sh = this.split.el.getHeight();
34852             box.height += sh;
34853             box.y -= sh;
34854         }
34855         return box;
34856     },
34857     
34858     updateBox : function(box){
34859         if(this.split && !this.collapsed){
34860             var sh = this.split.el.getHeight();
34861             box.height -= sh;
34862             box.y += sh;
34863             this.split.el.setLeft(box.x);
34864             this.split.el.setTop(box.y-sh);
34865             this.split.el.setWidth(box.width);
34866         }
34867         if(this.collapsed){
34868             this.updateBody(box.width, null);
34869         }
34870         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34871     }
34872 });
34873
34874 Roo.bootstrap.layout.East = function(config){
34875     config.region = "east";
34876     config.cursor = "e-resize";
34877     Roo.bootstrap.layout.Split.call(this, config);
34878     if(this.split){
34879         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34880         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34881         this.split.el.addClass("roo-layout-split-h");
34882     }
34883     var size = config.initialSize || config.width;
34884     if(typeof size != "undefined"){
34885         this.el.setWidth(size);
34886     }
34887 };
34888 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34889     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34890     getBox : function(){
34891         if(this.collapsed){
34892             return this.collapsedEl.getBox();
34893         }
34894         var box = this.el.getBox();
34895         if(this.split){
34896             var sw = this.split.el.getWidth();
34897             box.width += sw;
34898             box.x -= sw;
34899         }
34900         return box;
34901     },
34902
34903     updateBox : function(box){
34904         if(this.split && !this.collapsed){
34905             var sw = this.split.el.getWidth();
34906             box.width -= sw;
34907             this.split.el.setLeft(box.x);
34908             this.split.el.setTop(box.y);
34909             this.split.el.setHeight(box.height);
34910             box.x += sw;
34911         }
34912         if(this.collapsed){
34913             this.updateBody(null, box.height);
34914         }
34915         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34916     }
34917 });
34918
34919 Roo.bootstrap.layout.West = function(config){
34920     config.region = "west";
34921     config.cursor = "w-resize";
34922     
34923     Roo.bootstrap.layout.Split.call(this, config);
34924     if(this.split){
34925         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34926         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34927         this.split.el.addClass("roo-layout-split-h");
34928     }
34929     
34930 };
34931 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34932     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34933     
34934     onRender: function(ctr, pos)
34935     {
34936         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34937         var size = this.config.initialSize || this.config.width;
34938         if(typeof size != "undefined"){
34939             this.el.setWidth(size);
34940         }
34941     },
34942     
34943     getBox : function(){
34944         if(this.collapsed){
34945             return this.collapsedEl.getBox();
34946         }
34947         var box = this.el.getBox();
34948         if(this.split){
34949             box.width += this.split.el.getWidth();
34950         }
34951         return box;
34952     },
34953     
34954     updateBox : function(box){
34955         if(this.split && !this.collapsed){
34956             var sw = this.split.el.getWidth();
34957             box.width -= sw;
34958             this.split.el.setLeft(box.x+box.width);
34959             this.split.el.setTop(box.y);
34960             this.split.el.setHeight(box.height);
34961         }
34962         if(this.collapsed){
34963             this.updateBody(null, box.height);
34964         }
34965         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34966     }
34967 });
34968 Roo.namespace("Roo.bootstrap.panel");/*
34969  * Based on:
34970  * Ext JS Library 1.1.1
34971  * Copyright(c) 2006-2007, Ext JS, LLC.
34972  *
34973  * Originally Released Under LGPL - original licence link has changed is not relivant.
34974  *
34975  * Fork - LGPL
34976  * <script type="text/javascript">
34977  */
34978 /**
34979  * @class Roo.ContentPanel
34980  * @extends Roo.util.Observable
34981  * A basic ContentPanel element.
34982  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34983  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34984  * @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
34985  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34986  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34987  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34988  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34989  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34990  * @cfg {String} title          The title for this panel
34991  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34992  * @cfg {String} url            Calls {@link #setUrl} with this value
34993  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34994  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34995  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34996  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34997  * @cfg {Boolean} badges render the badges
34998
34999  * @constructor
35000  * Create a new ContentPanel.
35001  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35002  * @param {String/Object} config A string to set only the title or a config object
35003  * @param {String} content (optional) Set the HTML content for this panel
35004  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35005  */
35006 Roo.bootstrap.panel.Content = function( config){
35007     
35008     this.tpl = config.tpl || false;
35009     
35010     var el = config.el;
35011     var content = config.content;
35012
35013     if(config.autoCreate){ // xtype is available if this is called from factory
35014         el = Roo.id();
35015     }
35016     this.el = Roo.get(el);
35017     if(!this.el && config && config.autoCreate){
35018         if(typeof config.autoCreate == "object"){
35019             if(!config.autoCreate.id){
35020                 config.autoCreate.id = config.id||el;
35021             }
35022             this.el = Roo.DomHelper.append(document.body,
35023                         config.autoCreate, true);
35024         }else{
35025             var elcfg =  {   tag: "div",
35026                             cls: "roo-layout-inactive-content",
35027                             id: config.id||el
35028                             };
35029             if (config.html) {
35030                 elcfg.html = config.html;
35031                 
35032             }
35033                         
35034             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35035         }
35036     } 
35037     this.closable = false;
35038     this.loaded = false;
35039     this.active = false;
35040    
35041       
35042     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35043         
35044         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35045         
35046         this.wrapEl = this.el; //this.el.wrap();
35047         var ti = [];
35048         if (config.toolbar.items) {
35049             ti = config.toolbar.items ;
35050             delete config.toolbar.items ;
35051         }
35052         
35053         var nitems = [];
35054         this.toolbar.render(this.wrapEl, 'before');
35055         for(var i =0;i < ti.length;i++) {
35056           //  Roo.log(['add child', items[i]]);
35057             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35058         }
35059         this.toolbar.items = nitems;
35060         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35061         delete config.toolbar;
35062         
35063     }
35064     /*
35065     // xtype created footer. - not sure if will work as we normally have to render first..
35066     if (this.footer && !this.footer.el && this.footer.xtype) {
35067         if (!this.wrapEl) {
35068             this.wrapEl = this.el.wrap();
35069         }
35070     
35071         this.footer.container = this.wrapEl.createChild();
35072          
35073         this.footer = Roo.factory(this.footer, Roo);
35074         
35075     }
35076     */
35077     
35078      if(typeof config == "string"){
35079         this.title = config;
35080     }else{
35081         Roo.apply(this, config);
35082     }
35083     
35084     if(this.resizeEl){
35085         this.resizeEl = Roo.get(this.resizeEl, true);
35086     }else{
35087         this.resizeEl = this.el;
35088     }
35089     // handle view.xtype
35090     
35091  
35092     
35093     
35094     this.addEvents({
35095         /**
35096          * @event activate
35097          * Fires when this panel is activated. 
35098          * @param {Roo.ContentPanel} this
35099          */
35100         "activate" : true,
35101         /**
35102          * @event deactivate
35103          * Fires when this panel is activated. 
35104          * @param {Roo.ContentPanel} this
35105          */
35106         "deactivate" : true,
35107
35108         /**
35109          * @event resize
35110          * Fires when this panel is resized if fitToFrame is true.
35111          * @param {Roo.ContentPanel} this
35112          * @param {Number} width The width after any component adjustments
35113          * @param {Number} height The height after any component adjustments
35114          */
35115         "resize" : true,
35116         
35117          /**
35118          * @event render
35119          * Fires when this tab is created
35120          * @param {Roo.ContentPanel} this
35121          */
35122         "render" : true
35123         
35124         
35125         
35126     });
35127     
35128
35129     
35130     
35131     if(this.autoScroll){
35132         this.resizeEl.setStyle("overflow", "auto");
35133     } else {
35134         // fix randome scrolling
35135         //this.el.on('scroll', function() {
35136         //    Roo.log('fix random scolling');
35137         //    this.scrollTo('top',0); 
35138         //});
35139     }
35140     content = content || this.content;
35141     if(content){
35142         this.setContent(content);
35143     }
35144     if(config && config.url){
35145         this.setUrl(this.url, this.params, this.loadOnce);
35146     }
35147     
35148     
35149     
35150     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35151     
35152     if (this.view && typeof(this.view.xtype) != 'undefined') {
35153         this.view.el = this.el.appendChild(document.createElement("div"));
35154         this.view = Roo.factory(this.view); 
35155         this.view.render  &&  this.view.render(false, '');  
35156     }
35157     
35158     
35159     this.fireEvent('render', this);
35160 };
35161
35162 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35163     
35164     tabTip : '',
35165     
35166     setRegion : function(region){
35167         this.region = region;
35168         this.setActiveClass(region && !this.background);
35169     },
35170     
35171     
35172     setActiveClass: function(state)
35173     {
35174         if(state){
35175            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35176            this.el.setStyle('position','relative');
35177         }else{
35178            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35179            this.el.setStyle('position', 'absolute');
35180         } 
35181     },
35182     
35183     /**
35184      * Returns the toolbar for this Panel if one was configured. 
35185      * @return {Roo.Toolbar} 
35186      */
35187     getToolbar : function(){
35188         return this.toolbar;
35189     },
35190     
35191     setActiveState : function(active)
35192     {
35193         this.active = active;
35194         this.setActiveClass(active);
35195         if(!active){
35196             this.fireEvent("deactivate", this);
35197         }else{
35198             this.fireEvent("activate", this);
35199         }
35200     },
35201     /**
35202      * Updates this panel's element
35203      * @param {String} content The new content
35204      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35205     */
35206     setContent : function(content, loadScripts){
35207         this.el.update(content, loadScripts);
35208     },
35209
35210     ignoreResize : function(w, h){
35211         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35212             return true;
35213         }else{
35214             this.lastSize = {width: w, height: h};
35215             return false;
35216         }
35217     },
35218     /**
35219      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35220      * @return {Roo.UpdateManager} The UpdateManager
35221      */
35222     getUpdateManager : function(){
35223         return this.el.getUpdateManager();
35224     },
35225      /**
35226      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35227      * @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:
35228 <pre><code>
35229 panel.load({
35230     url: "your-url.php",
35231     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35232     callback: yourFunction,
35233     scope: yourObject, //(optional scope)
35234     discardUrl: false,
35235     nocache: false,
35236     text: "Loading...",
35237     timeout: 30,
35238     scripts: false
35239 });
35240 </code></pre>
35241      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35242      * 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.
35243      * @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}
35244      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35245      * @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.
35246      * @return {Roo.ContentPanel} this
35247      */
35248     load : function(){
35249         var um = this.el.getUpdateManager();
35250         um.update.apply(um, arguments);
35251         return this;
35252     },
35253
35254
35255     /**
35256      * 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.
35257      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35258      * @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)
35259      * @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)
35260      * @return {Roo.UpdateManager} The UpdateManager
35261      */
35262     setUrl : function(url, params, loadOnce){
35263         if(this.refreshDelegate){
35264             this.removeListener("activate", this.refreshDelegate);
35265         }
35266         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35267         this.on("activate", this.refreshDelegate);
35268         return this.el.getUpdateManager();
35269     },
35270     
35271     _handleRefresh : function(url, params, loadOnce){
35272         if(!loadOnce || !this.loaded){
35273             var updater = this.el.getUpdateManager();
35274             updater.update(url, params, this._setLoaded.createDelegate(this));
35275         }
35276     },
35277     
35278     _setLoaded : function(){
35279         this.loaded = true;
35280     }, 
35281     
35282     /**
35283      * Returns this panel's id
35284      * @return {String} 
35285      */
35286     getId : function(){
35287         return this.el.id;
35288     },
35289     
35290     /** 
35291      * Returns this panel's element - used by regiosn to add.
35292      * @return {Roo.Element} 
35293      */
35294     getEl : function(){
35295         return this.wrapEl || this.el;
35296     },
35297     
35298    
35299     
35300     adjustForComponents : function(width, height)
35301     {
35302         //Roo.log('adjustForComponents ');
35303         if(this.resizeEl != this.el){
35304             width -= this.el.getFrameWidth('lr');
35305             height -= this.el.getFrameWidth('tb');
35306         }
35307         if(this.toolbar){
35308             var te = this.toolbar.getEl();
35309             height -= te.getHeight();
35310             te.setWidth(width);
35311         }
35312         if(this.footer){
35313             var te = this.footer.getEl();
35314             Roo.log("footer:" + te.getHeight());
35315             
35316             height -= te.getHeight();
35317             te.setWidth(width);
35318         }
35319         
35320         
35321         if(this.adjustments){
35322             width += this.adjustments[0];
35323             height += this.adjustments[1];
35324         }
35325         return {"width": width, "height": height};
35326     },
35327     
35328     setSize : function(width, height){
35329         if(this.fitToFrame && !this.ignoreResize(width, height)){
35330             if(this.fitContainer && this.resizeEl != this.el){
35331                 this.el.setSize(width, height);
35332             }
35333             var size = this.adjustForComponents(width, height);
35334             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35335             this.fireEvent('resize', this, size.width, size.height);
35336         }
35337     },
35338     
35339     /**
35340      * Returns this panel's title
35341      * @return {String} 
35342      */
35343     getTitle : function(){
35344         return this.title;
35345     },
35346     
35347     /**
35348      * Set this panel's title
35349      * @param {String} title
35350      */
35351     setTitle : function(title){
35352         this.title = title;
35353         if(this.region){
35354             this.region.updatePanelTitle(this, title);
35355         }
35356     },
35357     
35358     /**
35359      * Returns true is this panel was configured to be closable
35360      * @return {Boolean} 
35361      */
35362     isClosable : function(){
35363         return this.closable;
35364     },
35365     
35366     beforeSlide : function(){
35367         this.el.clip();
35368         this.resizeEl.clip();
35369     },
35370     
35371     afterSlide : function(){
35372         this.el.unclip();
35373         this.resizeEl.unclip();
35374     },
35375     
35376     /**
35377      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35378      *   Will fail silently if the {@link #setUrl} method has not been called.
35379      *   This does not activate the panel, just updates its content.
35380      */
35381     refresh : function(){
35382         if(this.refreshDelegate){
35383            this.loaded = false;
35384            this.refreshDelegate();
35385         }
35386     },
35387     
35388     /**
35389      * Destroys this panel
35390      */
35391     destroy : function(){
35392         this.el.removeAllListeners();
35393         var tempEl = document.createElement("span");
35394         tempEl.appendChild(this.el.dom);
35395         tempEl.innerHTML = "";
35396         this.el.remove();
35397         this.el = null;
35398     },
35399     
35400     /**
35401      * form - if the content panel contains a form - this is a reference to it.
35402      * @type {Roo.form.Form}
35403      */
35404     form : false,
35405     /**
35406      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35407      *    This contains a reference to it.
35408      * @type {Roo.View}
35409      */
35410     view : false,
35411     
35412       /**
35413      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35414      * <pre><code>
35415
35416 layout.addxtype({
35417        xtype : 'Form',
35418        items: [ .... ]
35419    }
35420 );
35421
35422 </code></pre>
35423      * @param {Object} cfg Xtype definition of item to add.
35424      */
35425     
35426     
35427     getChildContainer: function () {
35428         return this.getEl();
35429     }
35430     
35431     
35432     /*
35433         var  ret = new Roo.factory(cfg);
35434         return ret;
35435         
35436         
35437         // add form..
35438         if (cfg.xtype.match(/^Form$/)) {
35439             
35440             var el;
35441             //if (this.footer) {
35442             //    el = this.footer.container.insertSibling(false, 'before');
35443             //} else {
35444                 el = this.el.createChild();
35445             //}
35446
35447             this.form = new  Roo.form.Form(cfg);
35448             
35449             
35450             if ( this.form.allItems.length) {
35451                 this.form.render(el.dom);
35452             }
35453             return this.form;
35454         }
35455         // should only have one of theses..
35456         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35457             // views.. should not be just added - used named prop 'view''
35458             
35459             cfg.el = this.el.appendChild(document.createElement("div"));
35460             // factory?
35461             
35462             var ret = new Roo.factory(cfg);
35463              
35464              ret.render && ret.render(false, ''); // render blank..
35465             this.view = ret;
35466             return ret;
35467         }
35468         return false;
35469     }
35470     \*/
35471 });
35472  
35473 /**
35474  * @class Roo.bootstrap.panel.Grid
35475  * @extends Roo.bootstrap.panel.Content
35476  * @constructor
35477  * Create a new GridPanel.
35478  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35479  * @param {Object} config A the config object
35480   
35481  */
35482
35483
35484
35485 Roo.bootstrap.panel.Grid = function(config)
35486 {
35487     
35488       
35489     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35490         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35491
35492     config.el = this.wrapper;
35493     //this.el = this.wrapper;
35494     
35495       if (config.container) {
35496         // ctor'ed from a Border/panel.grid
35497         
35498         
35499         this.wrapper.setStyle("overflow", "hidden");
35500         this.wrapper.addClass('roo-grid-container');
35501
35502     }
35503     
35504     
35505     if(config.toolbar){
35506         var tool_el = this.wrapper.createChild();    
35507         this.toolbar = Roo.factory(config.toolbar);
35508         var ti = [];
35509         if (config.toolbar.items) {
35510             ti = config.toolbar.items ;
35511             delete config.toolbar.items ;
35512         }
35513         
35514         var nitems = [];
35515         this.toolbar.render(tool_el);
35516         for(var i =0;i < ti.length;i++) {
35517           //  Roo.log(['add child', items[i]]);
35518             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35519         }
35520         this.toolbar.items = nitems;
35521         
35522         delete config.toolbar;
35523     }
35524     
35525     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35526     config.grid.scrollBody = true;;
35527     config.grid.monitorWindowResize = false; // turn off autosizing
35528     config.grid.autoHeight = false;
35529     config.grid.autoWidth = false;
35530     
35531     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35532     
35533     if (config.background) {
35534         // render grid on panel activation (if panel background)
35535         this.on('activate', function(gp) {
35536             if (!gp.grid.rendered) {
35537                 gp.grid.render(this.wrapper);
35538                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35539             }
35540         });
35541             
35542     } else {
35543         this.grid.render(this.wrapper);
35544         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35545
35546     }
35547     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35548     // ??? needed ??? config.el = this.wrapper;
35549     
35550     
35551     
35552   
35553     // xtype created footer. - not sure if will work as we normally have to render first..
35554     if (this.footer && !this.footer.el && this.footer.xtype) {
35555         
35556         var ctr = this.grid.getView().getFooterPanel(true);
35557         this.footer.dataSource = this.grid.dataSource;
35558         this.footer = Roo.factory(this.footer, Roo);
35559         this.footer.render(ctr);
35560         
35561     }
35562     
35563     
35564     
35565     
35566      
35567 };
35568
35569 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35570     getId : function(){
35571         return this.grid.id;
35572     },
35573     
35574     /**
35575      * Returns the grid for this panel
35576      * @return {Roo.bootstrap.Table} 
35577      */
35578     getGrid : function(){
35579         return this.grid;    
35580     },
35581     
35582     setSize : function(width, height){
35583         if(!this.ignoreResize(width, height)){
35584             var grid = this.grid;
35585             var size = this.adjustForComponents(width, height);
35586             var gridel = grid.getGridEl();
35587             gridel.setSize(size.width, size.height);
35588             /*
35589             var thd = grid.getGridEl().select('thead',true).first();
35590             var tbd = grid.getGridEl().select('tbody', true).first();
35591             if (tbd) {
35592                 tbd.setSize(width, height - thd.getHeight());
35593             }
35594             */
35595             grid.autoSize();
35596         }
35597     },
35598      
35599     
35600     
35601     beforeSlide : function(){
35602         this.grid.getView().scroller.clip();
35603     },
35604     
35605     afterSlide : function(){
35606         this.grid.getView().scroller.unclip();
35607     },
35608     
35609     destroy : function(){
35610         this.grid.destroy();
35611         delete this.grid;
35612         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35613     }
35614 });
35615
35616 /**
35617  * @class Roo.bootstrap.panel.Nest
35618  * @extends Roo.bootstrap.panel.Content
35619  * @constructor
35620  * Create a new Panel, that can contain a layout.Border.
35621  * 
35622  * 
35623  * @param {Roo.BorderLayout} layout The layout for this panel
35624  * @param {String/Object} config A string to set only the title or a config object
35625  */
35626 Roo.bootstrap.panel.Nest = function(config)
35627 {
35628     // construct with only one argument..
35629     /* FIXME - implement nicer consturctors
35630     if (layout.layout) {
35631         config = layout;
35632         layout = config.layout;
35633         delete config.layout;
35634     }
35635     if (layout.xtype && !layout.getEl) {
35636         // then layout needs constructing..
35637         layout = Roo.factory(layout, Roo);
35638     }
35639     */
35640     
35641     config.el =  config.layout.getEl();
35642     
35643     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35644     
35645     config.layout.monitorWindowResize = false; // turn off autosizing
35646     this.layout = config.layout;
35647     this.layout.getEl().addClass("roo-layout-nested-layout");
35648     
35649     
35650     
35651     
35652 };
35653
35654 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35655
35656     setSize : function(width, height){
35657         if(!this.ignoreResize(width, height)){
35658             var size = this.adjustForComponents(width, height);
35659             var el = this.layout.getEl();
35660             if (size.height < 1) {
35661                 el.setWidth(size.width);   
35662             } else {
35663                 el.setSize(size.width, size.height);
35664             }
35665             var touch = el.dom.offsetWidth;
35666             this.layout.layout();
35667             // ie requires a double layout on the first pass
35668             if(Roo.isIE && !this.initialized){
35669                 this.initialized = true;
35670                 this.layout.layout();
35671             }
35672         }
35673     },
35674     
35675     // activate all subpanels if not currently active..
35676     
35677     setActiveState : function(active){
35678         this.active = active;
35679         this.setActiveClass(active);
35680         
35681         if(!active){
35682             this.fireEvent("deactivate", this);
35683             return;
35684         }
35685         
35686         this.fireEvent("activate", this);
35687         // not sure if this should happen before or after..
35688         if (!this.layout) {
35689             return; // should not happen..
35690         }
35691         var reg = false;
35692         for (var r in this.layout.regions) {
35693             reg = this.layout.getRegion(r);
35694             if (reg.getActivePanel()) {
35695                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35696                 reg.setActivePanel(reg.getActivePanel());
35697                 continue;
35698             }
35699             if (!reg.panels.length) {
35700                 continue;
35701             }
35702             reg.showPanel(reg.getPanel(0));
35703         }
35704         
35705         
35706         
35707         
35708     },
35709     
35710     /**
35711      * Returns the nested BorderLayout for this panel
35712      * @return {Roo.BorderLayout} 
35713      */
35714     getLayout : function(){
35715         return this.layout;
35716     },
35717     
35718      /**
35719      * Adds a xtype elements to the layout of the nested panel
35720      * <pre><code>
35721
35722 panel.addxtype({
35723        xtype : 'ContentPanel',
35724        region: 'west',
35725        items: [ .... ]
35726    }
35727 );
35728
35729 panel.addxtype({
35730         xtype : 'NestedLayoutPanel',
35731         region: 'west',
35732         layout: {
35733            center: { },
35734            west: { }   
35735         },
35736         items : [ ... list of content panels or nested layout panels.. ]
35737    }
35738 );
35739 </code></pre>
35740      * @param {Object} cfg Xtype definition of item to add.
35741      */
35742     addxtype : function(cfg) {
35743         return this.layout.addxtype(cfg);
35744     
35745     }
35746 });        /*
35747  * Based on:
35748  * Ext JS Library 1.1.1
35749  * Copyright(c) 2006-2007, Ext JS, LLC.
35750  *
35751  * Originally Released Under LGPL - original licence link has changed is not relivant.
35752  *
35753  * Fork - LGPL
35754  * <script type="text/javascript">
35755  */
35756 /**
35757  * @class Roo.TabPanel
35758  * @extends Roo.util.Observable
35759  * A lightweight tab container.
35760  * <br><br>
35761  * Usage:
35762  * <pre><code>
35763 // basic tabs 1, built from existing content
35764 var tabs = new Roo.TabPanel("tabs1");
35765 tabs.addTab("script", "View Script");
35766 tabs.addTab("markup", "View Markup");
35767 tabs.activate("script");
35768
35769 // more advanced tabs, built from javascript
35770 var jtabs = new Roo.TabPanel("jtabs");
35771 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35772
35773 // set up the UpdateManager
35774 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35775 var updater = tab2.getUpdateManager();
35776 updater.setDefaultUrl("ajax1.htm");
35777 tab2.on('activate', updater.refresh, updater, true);
35778
35779 // Use setUrl for Ajax loading
35780 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35781 tab3.setUrl("ajax2.htm", null, true);
35782
35783 // Disabled tab
35784 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35785 tab4.disable();
35786
35787 jtabs.activate("jtabs-1");
35788  * </code></pre>
35789  * @constructor
35790  * Create a new TabPanel.
35791  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35792  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35793  */
35794 Roo.bootstrap.panel.Tabs = function(config){
35795     /**
35796     * The container element for this TabPanel.
35797     * @type Roo.Element
35798     */
35799     this.el = Roo.get(config.el);
35800     delete config.el;
35801     if(config){
35802         if(typeof config == "boolean"){
35803             this.tabPosition = config ? "bottom" : "top";
35804         }else{
35805             Roo.apply(this, config);
35806         }
35807     }
35808     
35809     if(this.tabPosition == "bottom"){
35810         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35811         this.el.addClass("roo-tabs-bottom");
35812     }
35813     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35814     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35815     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35816     if(Roo.isIE){
35817         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35818     }
35819     if(this.tabPosition != "bottom"){
35820         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35821          * @type Roo.Element
35822          */
35823         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35824         this.el.addClass("roo-tabs-top");
35825     }
35826     this.items = [];
35827
35828     this.bodyEl.setStyle("position", "relative");
35829
35830     this.active = null;
35831     this.activateDelegate = this.activate.createDelegate(this);
35832
35833     this.addEvents({
35834         /**
35835          * @event tabchange
35836          * Fires when the active tab changes
35837          * @param {Roo.TabPanel} this
35838          * @param {Roo.TabPanelItem} activePanel The new active tab
35839          */
35840         "tabchange": true,
35841         /**
35842          * @event beforetabchange
35843          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35844          * @param {Roo.TabPanel} this
35845          * @param {Object} e Set cancel to true on this object to cancel the tab change
35846          * @param {Roo.TabPanelItem} tab The tab being changed to
35847          */
35848         "beforetabchange" : true
35849     });
35850
35851     Roo.EventManager.onWindowResize(this.onResize, this);
35852     this.cpad = this.el.getPadding("lr");
35853     this.hiddenCount = 0;
35854
35855
35856     // toolbar on the tabbar support...
35857     if (this.toolbar) {
35858         alert("no toolbar support yet");
35859         this.toolbar  = false;
35860         /*
35861         var tcfg = this.toolbar;
35862         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35863         this.toolbar = new Roo.Toolbar(tcfg);
35864         if (Roo.isSafari) {
35865             var tbl = tcfg.container.child('table', true);
35866             tbl.setAttribute('width', '100%');
35867         }
35868         */
35869         
35870     }
35871    
35872
35873
35874     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35875 };
35876
35877 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35878     /*
35879      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35880      */
35881     tabPosition : "top",
35882     /*
35883      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35884      */
35885     currentTabWidth : 0,
35886     /*
35887      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35888      */
35889     minTabWidth : 40,
35890     /*
35891      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35892      */
35893     maxTabWidth : 250,
35894     /*
35895      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35896      */
35897     preferredTabWidth : 175,
35898     /*
35899      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35900      */
35901     resizeTabs : false,
35902     /*
35903      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35904      */
35905     monitorResize : true,
35906     /*
35907      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35908      */
35909     toolbar : false,
35910
35911     /**
35912      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35913      * @param {String} id The id of the div to use <b>or create</b>
35914      * @param {String} text The text for the tab
35915      * @param {String} content (optional) Content to put in the TabPanelItem body
35916      * @param {Boolean} closable (optional) True to create a close icon on the tab
35917      * @return {Roo.TabPanelItem} The created TabPanelItem
35918      */
35919     addTab : function(id, text, content, closable, tpl)
35920     {
35921         var item = new Roo.bootstrap.panel.TabItem({
35922             panel: this,
35923             id : id,
35924             text : text,
35925             closable : closable,
35926             tpl : tpl
35927         });
35928         this.addTabItem(item);
35929         if(content){
35930             item.setContent(content);
35931         }
35932         return item;
35933     },
35934
35935     /**
35936      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35937      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35938      * @return {Roo.TabPanelItem}
35939      */
35940     getTab : function(id){
35941         return this.items[id];
35942     },
35943
35944     /**
35945      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35946      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35947      */
35948     hideTab : function(id){
35949         var t = this.items[id];
35950         if(!t.isHidden()){
35951            t.setHidden(true);
35952            this.hiddenCount++;
35953            this.autoSizeTabs();
35954         }
35955     },
35956
35957     /**
35958      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35959      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35960      */
35961     unhideTab : function(id){
35962         var t = this.items[id];
35963         if(t.isHidden()){
35964            t.setHidden(false);
35965            this.hiddenCount--;
35966            this.autoSizeTabs();
35967         }
35968     },
35969
35970     /**
35971      * Adds an existing {@link Roo.TabPanelItem}.
35972      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35973      */
35974     addTabItem : function(item){
35975         this.items[item.id] = item;
35976         this.items.push(item);
35977       //  if(this.resizeTabs){
35978     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35979   //         this.autoSizeTabs();
35980 //        }else{
35981 //            item.autoSize();
35982        // }
35983     },
35984
35985     /**
35986      * Removes a {@link Roo.TabPanelItem}.
35987      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35988      */
35989     removeTab : function(id){
35990         var items = this.items;
35991         var tab = items[id];
35992         if(!tab) { return; }
35993         var index = items.indexOf(tab);
35994         if(this.active == tab && items.length > 1){
35995             var newTab = this.getNextAvailable(index);
35996             if(newTab) {
35997                 newTab.activate();
35998             }
35999         }
36000         this.stripEl.dom.removeChild(tab.pnode.dom);
36001         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36002             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36003         }
36004         items.splice(index, 1);
36005         delete this.items[tab.id];
36006         tab.fireEvent("close", tab);
36007         tab.purgeListeners();
36008         this.autoSizeTabs();
36009     },
36010
36011     getNextAvailable : function(start){
36012         var items = this.items;
36013         var index = start;
36014         // look for a next tab that will slide over to
36015         // replace the one being removed
36016         while(index < items.length){
36017             var item = items[++index];
36018             if(item && !item.isHidden()){
36019                 return item;
36020             }
36021         }
36022         // if one isn't found select the previous tab (on the left)
36023         index = start;
36024         while(index >= 0){
36025             var item = items[--index];
36026             if(item && !item.isHidden()){
36027                 return item;
36028             }
36029         }
36030         return null;
36031     },
36032
36033     /**
36034      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36035      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36036      */
36037     disableTab : function(id){
36038         var tab = this.items[id];
36039         if(tab && this.active != tab){
36040             tab.disable();
36041         }
36042     },
36043
36044     /**
36045      * Enables a {@link Roo.TabPanelItem} that is disabled.
36046      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36047      */
36048     enableTab : function(id){
36049         var tab = this.items[id];
36050         tab.enable();
36051     },
36052
36053     /**
36054      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36055      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36056      * @return {Roo.TabPanelItem} The TabPanelItem.
36057      */
36058     activate : function(id){
36059         var tab = this.items[id];
36060         if(!tab){
36061             return null;
36062         }
36063         if(tab == this.active || tab.disabled){
36064             return tab;
36065         }
36066         var e = {};
36067         this.fireEvent("beforetabchange", this, e, tab);
36068         if(e.cancel !== true && !tab.disabled){
36069             if(this.active){
36070                 this.active.hide();
36071             }
36072             this.active = this.items[id];
36073             this.active.show();
36074             this.fireEvent("tabchange", this, this.active);
36075         }
36076         return tab;
36077     },
36078
36079     /**
36080      * Gets the active {@link Roo.TabPanelItem}.
36081      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36082      */
36083     getActiveTab : function(){
36084         return this.active;
36085     },
36086
36087     /**
36088      * Updates the tab body element to fit the height of the container element
36089      * for overflow scrolling
36090      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36091      */
36092     syncHeight : function(targetHeight){
36093         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36094         var bm = this.bodyEl.getMargins();
36095         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36096         this.bodyEl.setHeight(newHeight);
36097         return newHeight;
36098     },
36099
36100     onResize : function(){
36101         if(this.monitorResize){
36102             this.autoSizeTabs();
36103         }
36104     },
36105
36106     /**
36107      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36108      */
36109     beginUpdate : function(){
36110         this.updating = true;
36111     },
36112
36113     /**
36114      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36115      */
36116     endUpdate : function(){
36117         this.updating = false;
36118         this.autoSizeTabs();
36119     },
36120
36121     /**
36122      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36123      */
36124     autoSizeTabs : function(){
36125         var count = this.items.length;
36126         var vcount = count - this.hiddenCount;
36127         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36128             return;
36129         }
36130         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36131         var availWidth = Math.floor(w / vcount);
36132         var b = this.stripBody;
36133         if(b.getWidth() > w){
36134             var tabs = this.items;
36135             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36136             if(availWidth < this.minTabWidth){
36137                 /*if(!this.sleft){    // incomplete scrolling code
36138                     this.createScrollButtons();
36139                 }
36140                 this.showScroll();
36141                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36142             }
36143         }else{
36144             if(this.currentTabWidth < this.preferredTabWidth){
36145                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36146             }
36147         }
36148     },
36149
36150     /**
36151      * Returns the number of tabs in this TabPanel.
36152      * @return {Number}
36153      */
36154      getCount : function(){
36155          return this.items.length;
36156      },
36157
36158     /**
36159      * Resizes all the tabs to the passed width
36160      * @param {Number} The new width
36161      */
36162     setTabWidth : function(width){
36163         this.currentTabWidth = width;
36164         for(var i = 0, len = this.items.length; i < len; i++) {
36165                 if(!this.items[i].isHidden()) {
36166                 this.items[i].setWidth(width);
36167             }
36168         }
36169     },
36170
36171     /**
36172      * Destroys this TabPanel
36173      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36174      */
36175     destroy : function(removeEl){
36176         Roo.EventManager.removeResizeListener(this.onResize, this);
36177         for(var i = 0, len = this.items.length; i < len; i++){
36178             this.items[i].purgeListeners();
36179         }
36180         if(removeEl === true){
36181             this.el.update("");
36182             this.el.remove();
36183         }
36184     },
36185     
36186     createStrip : function(container)
36187     {
36188         var strip = document.createElement("nav");
36189         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36190         container.appendChild(strip);
36191         return strip;
36192     },
36193     
36194     createStripList : function(strip)
36195     {
36196         // div wrapper for retard IE
36197         // returns the "tr" element.
36198         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36199         //'<div class="x-tabs-strip-wrap">'+
36200           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36201           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36202         return strip.firstChild; //.firstChild.firstChild.firstChild;
36203     },
36204     createBody : function(container)
36205     {
36206         var body = document.createElement("div");
36207         Roo.id(body, "tab-body");
36208         //Roo.fly(body).addClass("x-tabs-body");
36209         Roo.fly(body).addClass("tab-content");
36210         container.appendChild(body);
36211         return body;
36212     },
36213     createItemBody :function(bodyEl, id){
36214         var body = Roo.getDom(id);
36215         if(!body){
36216             body = document.createElement("div");
36217             body.id = id;
36218         }
36219         //Roo.fly(body).addClass("x-tabs-item-body");
36220         Roo.fly(body).addClass("tab-pane");
36221          bodyEl.insertBefore(body, bodyEl.firstChild);
36222         return body;
36223     },
36224     /** @private */
36225     createStripElements :  function(stripEl, text, closable, tpl)
36226     {
36227         var td = document.createElement("li"); // was td..
36228         
36229         
36230         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36231         
36232         
36233         stripEl.appendChild(td);
36234         /*if(closable){
36235             td.className = "x-tabs-closable";
36236             if(!this.closeTpl){
36237                 this.closeTpl = new Roo.Template(
36238                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36239                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36240                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36241                 );
36242             }
36243             var el = this.closeTpl.overwrite(td, {"text": text});
36244             var close = el.getElementsByTagName("div")[0];
36245             var inner = el.getElementsByTagName("em")[0];
36246             return {"el": el, "close": close, "inner": inner};
36247         } else {
36248         */
36249         // not sure what this is..
36250 //            if(!this.tabTpl){
36251                 //this.tabTpl = new Roo.Template(
36252                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36253                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36254                 //);
36255 //                this.tabTpl = new Roo.Template(
36256 //                   '<a href="#">' +
36257 //                   '<span unselectable="on"' +
36258 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36259 //                            ' >{text}</span></a>'
36260 //                );
36261 //                
36262 //            }
36263
36264
36265             var template = tpl || this.tabTpl || false;
36266             
36267             if(!template){
36268                 
36269                 template = new Roo.Template(
36270                    '<a href="#">' +
36271                    '<span unselectable="on"' +
36272                             (this.disableTooltips ? '' : ' title="{text}"') +
36273                             ' >{text}</span></a>'
36274                 );
36275             }
36276             
36277             switch (typeof(template)) {
36278                 case 'object' :
36279                     break;
36280                 case 'string' :
36281                     template = new Roo.Template(template);
36282                     break;
36283                 default :
36284                     break;
36285             }
36286             
36287             var el = template.overwrite(td, {"text": text});
36288             
36289             var inner = el.getElementsByTagName("span")[0];
36290             
36291             return {"el": el, "inner": inner};
36292             
36293     }
36294         
36295     
36296 });
36297
36298 /**
36299  * @class Roo.TabPanelItem
36300  * @extends Roo.util.Observable
36301  * Represents an individual item (tab plus body) in a TabPanel.
36302  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36303  * @param {String} id The id of this TabPanelItem
36304  * @param {String} text The text for the tab of this TabPanelItem
36305  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36306  */
36307 Roo.bootstrap.panel.TabItem = function(config){
36308     /**
36309      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36310      * @type Roo.TabPanel
36311      */
36312     this.tabPanel = config.panel;
36313     /**
36314      * The id for this TabPanelItem
36315      * @type String
36316      */
36317     this.id = config.id;
36318     /** @private */
36319     this.disabled = false;
36320     /** @private */
36321     this.text = config.text;
36322     /** @private */
36323     this.loaded = false;
36324     this.closable = config.closable;
36325
36326     /**
36327      * The body element for this TabPanelItem.
36328      * @type Roo.Element
36329      */
36330     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36331     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36332     this.bodyEl.setStyle("display", "block");
36333     this.bodyEl.setStyle("zoom", "1");
36334     //this.hideAction();
36335
36336     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36337     /** @private */
36338     this.el = Roo.get(els.el);
36339     this.inner = Roo.get(els.inner, true);
36340     this.textEl = Roo.get(this.el.dom.firstChild, true);
36341     this.pnode = Roo.get(els.el.parentNode, true);
36342     this.el.on("mousedown", this.onTabMouseDown, this);
36343     this.el.on("click", this.onTabClick, this);
36344     /** @private */
36345     if(config.closable){
36346         var c = Roo.get(els.close, true);
36347         c.dom.title = this.closeText;
36348         c.addClassOnOver("close-over");
36349         c.on("click", this.closeClick, this);
36350      }
36351
36352     this.addEvents({
36353          /**
36354          * @event activate
36355          * Fires when this tab becomes the active tab.
36356          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36357          * @param {Roo.TabPanelItem} this
36358          */
36359         "activate": true,
36360         /**
36361          * @event beforeclose
36362          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36363          * @param {Roo.TabPanelItem} this
36364          * @param {Object} e Set cancel to true on this object to cancel the close.
36365          */
36366         "beforeclose": true,
36367         /**
36368          * @event close
36369          * Fires when this tab is closed.
36370          * @param {Roo.TabPanelItem} this
36371          */
36372          "close": true,
36373         /**
36374          * @event deactivate
36375          * Fires when this tab is no longer the active tab.
36376          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36377          * @param {Roo.TabPanelItem} this
36378          */
36379          "deactivate" : true
36380     });
36381     this.hidden = false;
36382
36383     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36384 };
36385
36386 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36387            {
36388     purgeListeners : function(){
36389        Roo.util.Observable.prototype.purgeListeners.call(this);
36390        this.el.removeAllListeners();
36391     },
36392     /**
36393      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36394      */
36395     show : function(){
36396         this.pnode.addClass("active");
36397         this.showAction();
36398         if(Roo.isOpera){
36399             this.tabPanel.stripWrap.repaint();
36400         }
36401         this.fireEvent("activate", this.tabPanel, this);
36402     },
36403
36404     /**
36405      * Returns true if this tab is the active tab.
36406      * @return {Boolean}
36407      */
36408     isActive : function(){
36409         return this.tabPanel.getActiveTab() == this;
36410     },
36411
36412     /**
36413      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36414      */
36415     hide : function(){
36416         this.pnode.removeClass("active");
36417         this.hideAction();
36418         this.fireEvent("deactivate", this.tabPanel, this);
36419     },
36420
36421     hideAction : function(){
36422         this.bodyEl.hide();
36423         this.bodyEl.setStyle("position", "absolute");
36424         this.bodyEl.setLeft("-20000px");
36425         this.bodyEl.setTop("-20000px");
36426     },
36427
36428     showAction : function(){
36429         this.bodyEl.setStyle("position", "relative");
36430         this.bodyEl.setTop("");
36431         this.bodyEl.setLeft("");
36432         this.bodyEl.show();
36433     },
36434
36435     /**
36436      * Set the tooltip for the tab.
36437      * @param {String} tooltip The tab's tooltip
36438      */
36439     setTooltip : function(text){
36440         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36441             this.textEl.dom.qtip = text;
36442             this.textEl.dom.removeAttribute('title');
36443         }else{
36444             this.textEl.dom.title = text;
36445         }
36446     },
36447
36448     onTabClick : function(e){
36449         e.preventDefault();
36450         this.tabPanel.activate(this.id);
36451     },
36452
36453     onTabMouseDown : function(e){
36454         e.preventDefault();
36455         this.tabPanel.activate(this.id);
36456     },
36457 /*
36458     getWidth : function(){
36459         return this.inner.getWidth();
36460     },
36461
36462     setWidth : function(width){
36463         var iwidth = width - this.pnode.getPadding("lr");
36464         this.inner.setWidth(iwidth);
36465         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36466         this.pnode.setWidth(width);
36467     },
36468 */
36469     /**
36470      * Show or hide the tab
36471      * @param {Boolean} hidden True to hide or false to show.
36472      */
36473     setHidden : function(hidden){
36474         this.hidden = hidden;
36475         this.pnode.setStyle("display", hidden ? "none" : "");
36476     },
36477
36478     /**
36479      * Returns true if this tab is "hidden"
36480      * @return {Boolean}
36481      */
36482     isHidden : function(){
36483         return this.hidden;
36484     },
36485
36486     /**
36487      * Returns the text for this tab
36488      * @return {String}
36489      */
36490     getText : function(){
36491         return this.text;
36492     },
36493     /*
36494     autoSize : function(){
36495         //this.el.beginMeasure();
36496         this.textEl.setWidth(1);
36497         /*
36498          *  #2804 [new] Tabs in Roojs
36499          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36500          */
36501         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36502         //this.el.endMeasure();
36503     //},
36504
36505     /**
36506      * Sets the text for the tab (Note: this also sets the tooltip text)
36507      * @param {String} text The tab's text and tooltip
36508      */
36509     setText : function(text){
36510         this.text = text;
36511         this.textEl.update(text);
36512         this.setTooltip(text);
36513         //if(!this.tabPanel.resizeTabs){
36514         //    this.autoSize();
36515         //}
36516     },
36517     /**
36518      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36519      */
36520     activate : function(){
36521         this.tabPanel.activate(this.id);
36522     },
36523
36524     /**
36525      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36526      */
36527     disable : function(){
36528         if(this.tabPanel.active != this){
36529             this.disabled = true;
36530             this.pnode.addClass("disabled");
36531         }
36532     },
36533
36534     /**
36535      * Enables this TabPanelItem if it was previously disabled.
36536      */
36537     enable : function(){
36538         this.disabled = false;
36539         this.pnode.removeClass("disabled");
36540     },
36541
36542     /**
36543      * Sets the content for this TabPanelItem.
36544      * @param {String} content The content
36545      * @param {Boolean} loadScripts true to look for and load scripts
36546      */
36547     setContent : function(content, loadScripts){
36548         this.bodyEl.update(content, loadScripts);
36549     },
36550
36551     /**
36552      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36553      * @return {Roo.UpdateManager} The UpdateManager
36554      */
36555     getUpdateManager : function(){
36556         return this.bodyEl.getUpdateManager();
36557     },
36558
36559     /**
36560      * Set a URL to be used to load the content for this TabPanelItem.
36561      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36562      * @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)
36563      * @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)
36564      * @return {Roo.UpdateManager} The UpdateManager
36565      */
36566     setUrl : function(url, params, loadOnce){
36567         if(this.refreshDelegate){
36568             this.un('activate', this.refreshDelegate);
36569         }
36570         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36571         this.on("activate", this.refreshDelegate);
36572         return this.bodyEl.getUpdateManager();
36573     },
36574
36575     /** @private */
36576     _handleRefresh : function(url, params, loadOnce){
36577         if(!loadOnce || !this.loaded){
36578             var updater = this.bodyEl.getUpdateManager();
36579             updater.update(url, params, this._setLoaded.createDelegate(this));
36580         }
36581     },
36582
36583     /**
36584      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36585      *   Will fail silently if the setUrl method has not been called.
36586      *   This does not activate the panel, just updates its content.
36587      */
36588     refresh : function(){
36589         if(this.refreshDelegate){
36590            this.loaded = false;
36591            this.refreshDelegate();
36592         }
36593     },
36594
36595     /** @private */
36596     _setLoaded : function(){
36597         this.loaded = true;
36598     },
36599
36600     /** @private */
36601     closeClick : function(e){
36602         var o = {};
36603         e.stopEvent();
36604         this.fireEvent("beforeclose", this, o);
36605         if(o.cancel !== true){
36606             this.tabPanel.removeTab(this.id);
36607         }
36608     },
36609     /**
36610      * The text displayed in the tooltip for the close icon.
36611      * @type String
36612      */
36613     closeText : "Close this tab"
36614 });